1// Copyright 2012 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/ic.h" 6 7#include "src/api/api-arguments-inl.h" 8#include "src/api/api.h" 9#include "src/ast/ast.h" 10#include "src/base/bits.h" 11#include "src/base/logging.h" 12#include "src/builtins/accessors.h" 13#include "src/common/assert-scope.h" 14#include "src/common/globals.h" 15#include "src/execution/arguments-inl.h" 16#include "src/execution/execution.h" 17#include "src/execution/frames-inl.h" 18#include "src/execution/isolate-inl.h" 19#include "src/execution/protectors-inl.h" 20#include "src/execution/tiering-manager.h" 21#include "src/handles/handles-inl.h" 22#include "src/ic/call-optimization.h" 23#include "src/ic/handler-configuration-inl.h" 24#include "src/ic/ic-inl.h" 25#include "src/ic/ic-stats.h" 26#include "src/ic/stub-cache.h" 27#include "src/numbers/conversions.h" 28#include "src/objects/api-callbacks.h" 29#include "src/objects/data-handler-inl.h" 30#include "src/objects/field-type.h" 31#include "src/objects/hash-table-inl.h" 32#include "src/objects/heap-number-inl.h" 33#include "src/objects/instance-type.h" 34#include "src/objects/js-array-buffer-inl.h" 35#include "src/objects/js-array-inl.h" 36#include "src/objects/megadom-handler.h" 37#include "src/objects/module-inl.h" 38#include "src/objects/property-descriptor.h" 39#include "src/objects/prototype.h" 40#include "src/objects/struct-inl.h" 41#include "src/runtime/runtime-utils.h" 42#include "src/runtime/runtime.h" 43#include "src/tracing/trace-event.h" 44#include "src/tracing/tracing-category-observer.h" 45#include "src/utils/ostreams.h" 46 47#if V8_ENABLE_WEBASSEMBLY 48#include "src/wasm/struct-types.h" 49#endif // V8_ENABLE_WEBASSEMBLY 50 51namespace v8 { 52namespace internal { 53 54// Aliases to avoid having to repeat the class. 55// With C++20 we can use "using" to introduce scoped enums. 56constexpr InlineCacheState NO_FEEDBACK = InlineCacheState::NO_FEEDBACK; 57constexpr InlineCacheState UNINITIALIZED = InlineCacheState::UNINITIALIZED; 58constexpr InlineCacheState MONOMORPHIC = InlineCacheState::MONOMORPHIC; 59constexpr InlineCacheState RECOMPUTE_HANDLER = 60 InlineCacheState::RECOMPUTE_HANDLER; 61constexpr InlineCacheState POLYMORPHIC = InlineCacheState::POLYMORPHIC; 62constexpr InlineCacheState MEGAMORPHIC = InlineCacheState::MEGAMORPHIC; 63constexpr InlineCacheState MEGADOM = InlineCacheState::MEGADOM; 64constexpr InlineCacheState GENERIC = InlineCacheState::GENERIC; 65 66char IC::TransitionMarkFromState(IC::State state) { 67 switch (state) { 68 case NO_FEEDBACK: 69 return 'X'; 70 case UNINITIALIZED: 71 return '0'; 72 case MONOMORPHIC: 73 return '1'; 74 case RECOMPUTE_HANDLER: 75 return '^'; 76 case POLYMORPHIC: 77 return 'P'; 78 case MEGAMORPHIC: 79 return 'N'; 80 case MEGADOM: 81 return 'D'; 82 case GENERIC: 83 return 'G'; 84 } 85 UNREACHABLE(); 86} 87 88namespace { 89 90const char* GetModifier(KeyedAccessLoadMode mode) { 91 if (mode == LOAD_IGNORE_OUT_OF_BOUNDS) return ".IGNORE_OOB"; 92 return ""; 93} 94 95const char* GetModifier(KeyedAccessStoreMode mode) { 96 switch (mode) { 97 case STORE_HANDLE_COW: 98 return ".COW"; 99 case STORE_AND_GROW_HANDLE_COW: 100 return ".STORE+COW"; 101 case STORE_IGNORE_OUT_OF_BOUNDS: 102 return ".IGNORE_OOB"; 103 case STANDARD_STORE: 104 return ""; 105 } 106 UNREACHABLE(); 107} 108 109} // namespace 110 111void IC::TraceIC(const char* type, Handle<Object> name) { 112 if (V8_LIKELY(!TracingFlags::is_ic_stats_enabled())) return; 113 State new_state = 114 (state() == NO_FEEDBACK) ? NO_FEEDBACK : nexus()->ic_state(); 115 TraceIC(type, name, state(), new_state); 116} 117 118void IC::TraceIC(const char* type, Handle<Object> name, State old_state, 119 State new_state) { 120 if (V8_LIKELY(!TracingFlags::is_ic_stats_enabled())) return; 121 122 Handle<Map> map = lookup_start_object_map(); // Might be empty. 123 124 const char* modifier = ""; 125 if (state() == NO_FEEDBACK) { 126 modifier = ""; 127 } else if (IsKeyedLoadIC()) { 128 KeyedAccessLoadMode mode = nexus()->GetKeyedAccessLoadMode(); 129 modifier = GetModifier(mode); 130 } else if (IsKeyedStoreIC() || IsStoreInArrayLiteralIC() || 131 IsDefineKeyedOwnIC()) { 132 KeyedAccessStoreMode mode = nexus()->GetKeyedAccessStoreMode(); 133 modifier = GetModifier(mode); 134 } 135 136 bool keyed_prefix = is_keyed() && !IsStoreInArrayLiteralIC(); 137 138 if (!(TracingFlags::ic_stats.load(std::memory_order_relaxed) & 139 v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) { 140 LOG(isolate(), ICEvent(type, keyed_prefix, map, name, 141 TransitionMarkFromState(old_state), 142 TransitionMarkFromState(new_state), modifier, 143 slow_stub_reason_)); 144 return; 145 } 146 147 JavaScriptFrameIterator it(isolate()); 148 JavaScriptFrame* frame = it.frame(); 149 150 DisallowGarbageCollection no_gc; 151 JSFunction function = frame->function(); 152 153 ICStats::instance()->Begin(); 154 ICInfo& ic_info = ICStats::instance()->Current(); 155 ic_info.type = keyed_prefix ? "Keyed" : ""; 156 ic_info.type += type; 157 158 int code_offset = 0; 159 AbstractCode code = function.abstract_code(isolate_); 160 if (function.ActiveTierIsIgnition()) { 161 code_offset = InterpretedFrame::GetBytecodeOffset(frame->fp()); 162 } else if (function.ActiveTierIsBaseline()) { 163 // TODO(pthier): AbstractCode should fully support Baseline code. 164 BaselineFrame* baseline_frame = BaselineFrame::cast(frame); 165 code_offset = baseline_frame->GetBytecodeOffset(); 166 code = AbstractCode::cast(baseline_frame->GetBytecodeArray()); 167 } else { 168 code_offset = static_cast<int>(frame->pc() - function.code_entry_point()); 169 } 170 JavaScriptFrame::CollectFunctionAndOffsetForICStats(function, code, 171 code_offset); 172 173 // Reserve enough space for IC transition state, the longest length is 17. 174 ic_info.state.reserve(17); 175 ic_info.state = "("; 176 ic_info.state += TransitionMarkFromState(old_state); 177 ic_info.state += "->"; 178 ic_info.state += TransitionMarkFromState(new_state); 179 ic_info.state += modifier; 180 ic_info.state += ")"; 181 if (!map.is_null()) { 182 ic_info.map = reinterpret_cast<void*>(map->ptr()); 183 ic_info.is_dictionary_map = map->is_dictionary_map(); 184 ic_info.number_of_own_descriptors = map->NumberOfOwnDescriptors(); 185 ic_info.instance_type = std::to_string(map->instance_type()); 186 } else { 187 ic_info.map = nullptr; 188 } 189 // TODO(lpy) Add name as key field in ICStats. 190 ICStats::instance()->End(); 191} 192 193IC::IC(Isolate* isolate, Handle<FeedbackVector> vector, FeedbackSlot slot, 194 FeedbackSlotKind kind) 195 : isolate_(isolate), 196 vector_set_(false), 197 kind_(kind), 198 target_maps_set_(false), 199 slow_stub_reason_(nullptr), 200 nexus_(vector, slot) { 201 DCHECK_IMPLIES(!vector.is_null(), kind_ == nexus_.kind()); 202 state_ = (vector.is_null()) ? NO_FEEDBACK : nexus_.ic_state(); 203 old_state_ = state_; 204} 205 206static void LookupForRead(LookupIterator* it, bool is_has_property) { 207 for (; it->IsFound(); it->Next()) { 208 switch (it->state()) { 209 case LookupIterator::NOT_FOUND: 210 case LookupIterator::TRANSITION: 211 UNREACHABLE(); 212 case LookupIterator::JSPROXY: 213 return; 214 case LookupIterator::INTERCEPTOR: { 215 // If there is a getter, return; otherwise loop to perform the lookup. 216 Handle<JSObject> holder = it->GetHolder<JSObject>(); 217 if (!holder->GetNamedInterceptor().getter().IsUndefined( 218 it->isolate())) { 219 return; 220 } 221 if (is_has_property && 222 !holder->GetNamedInterceptor().query().IsUndefined(it->isolate())) { 223 return; 224 } 225 break; 226 } 227 case LookupIterator::ACCESS_CHECK: 228 // ICs know how to perform access checks on global proxies. 229 if (it->GetHolder<JSObject>()->IsJSGlobalProxy() && it->HasAccess()) { 230 break; 231 } 232 return; 233 case LookupIterator::ACCESSOR: 234 case LookupIterator::INTEGER_INDEXED_EXOTIC: 235 case LookupIterator::DATA: 236 return; 237 } 238 } 239} 240 241bool IC::ShouldRecomputeHandler(Handle<String> name) { 242 if (!RecomputeHandlerForName(name)) return false; 243 244 // This is a contextual access, always just update the handler and stay 245 // monomorphic. 246 if (IsGlobalIC()) return true; 247 248 MaybeObjectHandle maybe_handler = 249 nexus()->FindHandlerForMap(lookup_start_object_map()); 250 251 // The current map wasn't handled yet. There's no reason to stay monomorphic, 252 // *unless* we're moving from a deprecated map to its replacement, or 253 // to a more general elements kind. 254 // TODO(verwaest): Check if the current map is actually what the old map 255 // would transition to. 256 if (maybe_handler.is_null()) { 257 if (!lookup_start_object_map()->IsJSObjectMap()) return false; 258 Map first_map = FirstTargetMap(); 259 if (first_map.is_null()) return false; 260 Handle<Map> old_map(first_map, isolate()); 261 if (old_map->is_deprecated()) return true; 262 return IsMoreGeneralElementsKindTransition( 263 old_map->elements_kind(), lookup_start_object_map()->elements_kind()); 264 } 265 266 return true; 267} 268 269bool IC::RecomputeHandlerForName(Handle<Object> name) { 270 if (is_keyed()) { 271 // Determine whether the failure is due to a name failure. 272 if (!name->IsName()) return false; 273 Name stub_name = nexus()->GetName(); 274 if (*name != stub_name) return false; 275 } 276 277 return true; 278} 279 280void IC::UpdateState(Handle<Object> lookup_start_object, Handle<Object> name) { 281 if (state() == NO_FEEDBACK) return; 282 update_lookup_start_object_map(lookup_start_object); 283 if (!name->IsString()) return; 284 if (state() != MONOMORPHIC && state() != POLYMORPHIC) return; 285 if (lookup_start_object->IsNullOrUndefined(isolate())) return; 286 287 // Remove the target from the code cache if it became invalid 288 // because of changes in the prototype chain to avoid hitting it 289 // again. 290 if (ShouldRecomputeHandler(Handle<String>::cast(name))) { 291 MarkRecomputeHandler(name); 292 } 293} 294 295MaybeHandle<Object> IC::TypeError(MessageTemplate index, Handle<Object> object, 296 Handle<Object> key) { 297 HandleScope scope(isolate()); 298 THROW_NEW_ERROR(isolate(), NewTypeError(index, key, object), Object); 299} 300 301MaybeHandle<Object> IC::ReferenceError(Handle<Name> name) { 302 HandleScope scope(isolate()); 303 THROW_NEW_ERROR( 304 isolate(), NewReferenceError(MessageTemplate::kNotDefined, name), Object); 305} 306 307void IC::OnFeedbackChanged(const char* reason) { 308 vector_set_ = true; 309 FeedbackVector vector = nexus()->vector(); 310 FeedbackSlot slot = nexus()->slot(); 311 OnFeedbackChanged(isolate(), vector, slot, reason); 312} 313 314// static 315void IC::OnFeedbackChanged(Isolate* isolate, FeedbackVector vector, 316 FeedbackSlot slot, const char* reason) { 317 if (FLAG_trace_opt_verbose) { 318 if (vector.profiler_ticks() != 0) { 319 StdoutStream os; 320 os << "[resetting ticks for "; 321 vector.shared_function_info().ShortPrint(os); 322 os << " from " << vector.profiler_ticks() 323 << " due to IC change: " << reason << "]" << std::endl; 324 } 325 } 326 vector.set_profiler_ticks(0); 327 328#ifdef V8_TRACE_FEEDBACK_UPDATES 329 if (FLAG_trace_feedback_updates) { 330 int slot_count = vector.metadata().slot_count(); 331 StdoutStream os; 332 if (slot.IsInvalid()) { 333 os << "[Feedback slots in "; 334 } else { 335 os << "[Feedback slot " << slot.ToInt() << "/" << slot_count << " in "; 336 } 337 vector.shared_function_info().ShortPrint(os); 338 if (slot.IsInvalid()) { 339 os << " updated - "; 340 } else { 341 os << " updated to "; 342 vector.FeedbackSlotPrint(os, slot); 343 os << " - "; 344 } 345 os << reason << "]" << std::endl; 346 } 347#endif 348 349 isolate->tiering_manager()->NotifyICChanged(); 350} 351 352namespace { 353 354bool MigrateDeprecated(Isolate* isolate, Handle<Object> object) { 355 if (!object->IsJSObject()) return false; 356 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 357 if (!receiver->map().is_deprecated()) return false; 358 JSObject::MigrateInstance(isolate, receiver); 359 return true; 360} 361 362} // namespace 363 364bool IC::ConfigureVectorState(IC::State new_state, Handle<Object> key) { 365 DCHECK_EQ(MEGAMORPHIC, new_state); 366 DCHECK_IMPLIES(!is_keyed(), key->IsName()); 367 // Even though we don't change the feedback data, we still want to reset the 368 // profiler ticks. Real-world observations suggest that optimizing these 369 // functions doesn't improve performance. 370 bool changed = nexus()->ConfigureMegamorphic( 371 key->IsName() ? IcCheckType::kProperty : IcCheckType::kElement); 372 OnFeedbackChanged("Megamorphic"); 373 return changed; 374} 375 376void IC::ConfigureVectorState(Handle<Name> name, Handle<Map> map, 377 Handle<Object> handler) { 378 ConfigureVectorState(name, map, MaybeObjectHandle(handler)); 379} 380 381void IC::ConfigureVectorState(Handle<Name> name, Handle<Map> map, 382 const MaybeObjectHandle& handler) { 383 if (IsGlobalIC()) { 384 nexus()->ConfigureHandlerMode(handler); 385 } else { 386 // Non-keyed ICs don't track the name explicitly. 387 if (!is_keyed()) name = Handle<Name>::null(); 388 nexus()->ConfigureMonomorphic(name, map, handler); 389 } 390 391 OnFeedbackChanged(IsLoadGlobalIC() ? "LoadGlobal" : "Monomorphic"); 392} 393 394void IC::ConfigureVectorState(Handle<Name> name, MapHandles const& maps, 395 MaybeObjectHandles* handlers) { 396 DCHECK(!IsGlobalIC()); 397 std::vector<MapAndHandler> maps_and_handlers; 398 DCHECK_EQ(maps.size(), handlers->size()); 399 for (size_t i = 0; i < maps.size(); i++) { 400 maps_and_handlers.push_back(MapAndHandler(maps[i], handlers->at(i))); 401 } 402 ConfigureVectorState(name, maps_and_handlers); 403} 404 405void IC::ConfigureVectorState( 406 Handle<Name> name, std::vector<MapAndHandler> const& maps_and_handlers) { 407 DCHECK(!IsGlobalIC()); 408 // Non-keyed ICs don't track the name explicitly. 409 if (!is_keyed()) name = Handle<Name>::null(); 410 nexus()->ConfigurePolymorphic(name, maps_and_handlers); 411 412 OnFeedbackChanged("Polymorphic"); 413} 414 415MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name, 416 bool update_feedback, 417 Handle<Object> receiver) { 418 bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic && update_feedback; 419 420 if (receiver.is_null()) { 421 receiver = object; 422 } 423 424 // If the object is undefined or null it's illegal to try to get any 425 // of its properties; throw a TypeError in that case. 426 if (IsAnyHas() ? !object->IsJSReceiver() 427 : object->IsNullOrUndefined(isolate())) { 428 if (use_ic) { 429 // Ensure the IC state progresses. 430 TRACE_HANDLER_STATS(isolate(), LoadIC_NonReceiver); 431 update_lookup_start_object_map(object); 432 SetCache(name, LoadHandler::LoadSlow(isolate())); 433 TraceIC("LoadIC", name); 434 } 435 436 if (*name == ReadOnlyRoots(isolate()).iterator_symbol()) { 437 return isolate()->Throw<Object>( 438 ErrorUtils::NewIteratorError(isolate(), object)); 439 } 440 441 if (IsAnyHas()) { 442 return TypeError(MessageTemplate::kInvalidInOperatorUse, object, name); 443 } else { 444 DCHECK(object->IsNullOrUndefined(isolate())); 445 ErrorUtils::ThrowLoadFromNullOrUndefined(isolate(), object, name); 446 return MaybeHandle<Object>(); 447 } 448 } 449 450 // If we encounter an object with a deprecated map, we want to update the 451 // feedback vector with the migrated map. 452 // Mark ourselves as RECOMPUTE_HANDLER so that we don't turn megamorphic due 453 // to seeing the same map and handler. 454 if (MigrateDeprecated(isolate(), object)) { 455 UpdateState(object, name); 456 } 457 458 JSObject::MakePrototypesFast(object, kStartAtReceiver, isolate()); 459 update_lookup_start_object_map(object); 460 461 PropertyKey key(isolate(), name); 462 LookupIterator it = LookupIterator(isolate(), receiver, key, object); 463 464 // Named lookup in the object. 465 LookupForRead(&it, IsAnyHas()); 466 467 if (name->IsPrivate()) { 468 Handle<Symbol> private_symbol = Handle<Symbol>::cast(name); 469 if (!IsAnyHas() && private_symbol->is_private_name() && !it.IsFound()) { 470 Handle<String> name_string(String::cast(private_symbol->description()), 471 isolate()); 472 if (private_symbol->is_private_brand()) { 473 Handle<String> class_name = 474 (name_string->length() == 0) 475 ? isolate()->factory()->anonymous_string() 476 : name_string; 477 return TypeError(MessageTemplate::kInvalidPrivateBrandInstance, object, 478 class_name); 479 } 480 return TypeError(MessageTemplate::kInvalidPrivateMemberRead, object, 481 name_string); 482 } 483 484 // IC handling of private symbols/fields lookup on JSProxy is not 485 // supported. 486 if (object->IsJSProxy()) { 487 use_ic = false; 488 } 489 } 490 491 if (it.IsFound() || !ShouldThrowReferenceError()) { 492 // Update inline cache and stub cache. 493 if (use_ic) { 494 UpdateCaches(&it); 495 } else if (state() == NO_FEEDBACK) { 496 // Tracing IC stats 497 IsLoadGlobalIC() ? TraceIC("LoadGlobalIC", name) 498 : TraceIC("LoadIC", name); 499 } 500 501 if (IsAnyHas()) { 502 // Named lookup in the object. 503 Maybe<bool> maybe = JSReceiver::HasProperty(&it); 504 if (maybe.IsNothing()) return MaybeHandle<Object>(); 505 return maybe.FromJust() ? ReadOnlyRoots(isolate()).true_value_handle() 506 : ReadOnlyRoots(isolate()).false_value_handle(); 507 } 508 509 // Get the property. 510 Handle<Object> result; 511 512 ASSIGN_RETURN_ON_EXCEPTION( 513 isolate(), result, Object::GetProperty(&it, IsLoadGlobalIC()), Object); 514 if (it.IsFound()) { 515 return result; 516 } else if (!ShouldThrowReferenceError()) { 517 return result; 518 } 519 } 520 return ReferenceError(name); 521} 522 523MaybeHandle<Object> LoadGlobalIC::Load(Handle<Name> name, 524 bool update_feedback) { 525 Handle<JSGlobalObject> global = isolate()->global_object(); 526 527 if (name->IsString()) { 528 // Look up in script context table. 529 Handle<String> str_name = Handle<String>::cast(name); 530 Handle<ScriptContextTable> script_contexts( 531 global->native_context().script_context_table(), isolate()); 532 533 VariableLookupResult lookup_result; 534 if (script_contexts->Lookup(str_name, &lookup_result)) { 535 Handle<Context> script_context = ScriptContextTable::GetContext( 536 isolate(), script_contexts, lookup_result.context_index); 537 538 Handle<Object> result(script_context->get(lookup_result.slot_index), 539 isolate()); 540 541 if (result->IsTheHole(isolate())) { 542 // Do not install stubs and stay pre-monomorphic for 543 // uninitialized accesses. 544 THROW_NEW_ERROR( 545 isolate(), 546 NewReferenceError(MessageTemplate::kAccessedUninitializedVariable, 547 name), 548 Object); 549 } 550 551 bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic && update_feedback; 552 if (use_ic) { 553 // 'const' Variables are mutable if REPL mode is enabled. This disables 554 // compiler inlining for all 'const' variables declared in REPL mode. 555 if (nexus()->ConfigureLexicalVarMode( 556 lookup_result.context_index, lookup_result.slot_index, 557 (lookup_result.mode == VariableMode::kConst && 558 !lookup_result.is_repl_mode))) { 559 TRACE_HANDLER_STATS(isolate(), LoadGlobalIC_LoadScriptContextField); 560 } else { 561 // Given combination of indices can't be encoded, so use slow stub. 562 TRACE_HANDLER_STATS(isolate(), LoadGlobalIC_SlowStub); 563 SetCache(name, LoadHandler::LoadSlow(isolate())); 564 } 565 TraceIC("LoadGlobalIC", name); 566 } else if (state() == NO_FEEDBACK) { 567 TraceIC("LoadGlobalIC", name); 568 } 569 return result; 570 } 571 } 572 return LoadIC::Load(global, name, update_feedback); 573} 574 575static bool AddOneReceiverMapIfMissing(MapHandles* receiver_maps, 576 Handle<Map> new_receiver_map) { 577 DCHECK(!new_receiver_map.is_null()); 578 for (Handle<Map> map : *receiver_maps) { 579 if (!map.is_null() && map.is_identical_to(new_receiver_map)) { 580 return false; 581 } 582 } 583 receiver_maps->push_back(new_receiver_map); 584 return true; 585} 586 587static bool AddOneReceiverMapIfMissing( 588 std::vector<MapAndHandler>* receiver_maps_and_handlers, 589 Handle<Map> new_receiver_map) { 590 DCHECK(!new_receiver_map.is_null()); 591 if (new_receiver_map->is_deprecated()) return false; 592 for (MapAndHandler map_and_handler : *receiver_maps_and_handlers) { 593 Handle<Map> map = map_and_handler.first; 594 if (!map.is_null() && map.is_identical_to(new_receiver_map)) { 595 return false; 596 } 597 } 598 receiver_maps_and_handlers->push_back( 599 MapAndHandler(new_receiver_map, MaybeObjectHandle())); 600 return true; 601} 602 603bool IC::UpdateMegaDOMIC(const MaybeObjectHandle& handler, Handle<Name> name) { 604 if (!FLAG_enable_mega_dom_ic) return false; 605 606 // TODO(gsathya): Enable fuzzing once this feature is more stable. 607 if (FLAG_fuzzing) return false; 608 609 // TODO(gsathya): Support KeyedLoadIC, StoreIC and KeyedStoreIC. 610 if (!IsLoadIC()) return false; 611 612 // Check if DOM protector cell is valid. 613 if (!Protectors::IsMegaDOMIntact(isolate())) return false; 614 615 // Check if current lookup object is an API object 616 Handle<Map> map = lookup_start_object_map(); 617 if (!InstanceTypeChecker::IsJSApiObject(map->instance_type())) return false; 618 619 Handle<Object> accessor_obj; 620 // TODO(gsathya): Check if there are overloads possible for this accessor and 621 // transition only if it isn't possible. 622 if (!accessor().ToHandle(&accessor_obj)) return false; 623 624 // TODO(gsathya): This is also created in IC::ComputeHandler, find a way to 625 // reuse it here. 626 CallOptimization call_optimization(isolate(), accessor_obj); 627 628 // Check if accessor is an API function 629 if (!call_optimization.is_simple_api_call()) return false; 630 631 // Check if accessor requires access checks 632 if (call_optimization.accept_any_receiver()) return false; 633 634 // Check if accessor requires signature checks 635 if (!call_optimization.requires_signature_check()) return false; 636 637 // Check if the receiver is the holder 638 CallOptimization::HolderLookup holder_lookup; 639 call_optimization.LookupHolderOfExpectedType(isolate(), map, &holder_lookup); 640 if (holder_lookup != CallOptimization::kHolderIsReceiver) return false; 641 642 Handle<Context> accessor_context(call_optimization.GetAccessorContext(*map), 643 isolate()); 644 645 Handle<MegaDomHandler> new_handler = isolate()->factory()->NewMegaDomHandler( 646 MaybeObjectHandle::Weak(accessor_obj), 647 MaybeObjectHandle::Weak(accessor_context)); 648 nexus()->ConfigureMegaDOM(MaybeObjectHandle(new_handler)); 649 return true; 650} 651 652bool IC::UpdatePolymorphicIC(Handle<Name> name, 653 const MaybeObjectHandle& handler) { 654 DCHECK(IsHandler(*handler)); 655 if (is_keyed() && state() != RECOMPUTE_HANDLER) { 656 if (nexus()->GetName() != *name) return false; 657 } 658 Handle<Map> map = lookup_start_object_map(); 659 660 std::vector<MapAndHandler> maps_and_handlers; 661 maps_and_handlers.reserve(FLAG_max_valid_polymorphic_map_count); 662 int deprecated_maps = 0; 663 int handler_to_overwrite = -1; 664 665 { 666 DisallowGarbageCollection no_gc; 667 int i = 0; 668 for (FeedbackIterator it(nexus()); !it.done(); it.Advance()) { 669 if (it.handler()->IsCleared()) continue; 670 MaybeObjectHandle existing_handler = handle(it.handler(), isolate()); 671 Handle<Map> existing_map = handle(it.map(), isolate()); 672 673 maps_and_handlers.push_back( 674 MapAndHandler(existing_map, existing_handler)); 675 676 if (existing_map->is_deprecated()) { 677 // Filter out deprecated maps to ensure their instances get migrated. 678 deprecated_maps++; 679 } else if (map.is_identical_to(existing_map)) { 680 // If both map and handler stayed the same (and the name is also the 681 // same as checked above, for keyed accesses), we're not progressing 682 // in the lattice and need to go MEGAMORPHIC instead. There's one 683 // exception to this rule, which is when we're in RECOMPUTE_HANDLER 684 // state, there we allow to migrate to a new handler. 685 if (handler.is_identical_to(existing_handler) && 686 state() != RECOMPUTE_HANDLER) { 687 return false; 688 } 689 690 // If the receiver type is already in the polymorphic IC, this indicates 691 // there was a prototoype chain failure. In that case, just overwrite 692 // the handler. 693 handler_to_overwrite = i; 694 } else if (handler_to_overwrite == -1 && 695 IsTransitionOfMonomorphicTarget(*existing_map, *map)) { 696 handler_to_overwrite = i; 697 } 698 699 i++; 700 } 701 DCHECK_LE(i, maps_and_handlers.size()); 702 } 703 704 int number_of_maps = static_cast<int>(maps_and_handlers.size()); 705 int number_of_valid_maps = 706 number_of_maps - deprecated_maps - (handler_to_overwrite != -1); 707 708 if (number_of_valid_maps >= FLAG_max_valid_polymorphic_map_count) 709 return false; 710 if (number_of_maps == 0 && state() != MONOMORPHIC && state() != POLYMORPHIC) { 711 return false; 712 } 713 714 number_of_valid_maps++; 715 if (number_of_valid_maps == 1) { 716 ConfigureVectorState(name, lookup_start_object_map(), handler); 717 } else { 718 if (is_keyed() && nexus()->GetName() != *name) return false; 719 if (handler_to_overwrite >= 0) { 720 maps_and_handlers[handler_to_overwrite].second = handler; 721 if (!map.is_identical_to( 722 maps_and_handlers.at(handler_to_overwrite).first)) { 723 maps_and_handlers[handler_to_overwrite].first = map; 724 } 725 } else { 726 maps_and_handlers.push_back(MapAndHandler(map, handler)); 727 } 728 729 ConfigureVectorState(name, maps_and_handlers); 730 } 731 732 return true; 733} 734 735void IC::UpdateMonomorphicIC(const MaybeObjectHandle& handler, 736 Handle<Name> name) { 737 DCHECK(IsHandler(*handler)); 738 ConfigureVectorState(name, lookup_start_object_map(), handler); 739} 740 741void IC::CopyICToMegamorphicCache(Handle<Name> name) { 742 std::vector<MapAndHandler> maps_and_handlers; 743 nexus()->ExtractMapsAndHandlers(&maps_and_handlers); 744 for (const MapAndHandler& map_and_handler : maps_and_handlers) { 745 UpdateMegamorphicCache(map_and_handler.first, name, map_and_handler.second); 746 } 747} 748 749bool IC::IsTransitionOfMonomorphicTarget(Map source_map, Map target_map) { 750 if (source_map.is_null()) return true; 751 if (target_map.is_null()) return false; 752 if (source_map.is_abandoned_prototype_map()) return false; 753 ElementsKind target_elements_kind = target_map.elements_kind(); 754 bool more_general_transition = IsMoreGeneralElementsKindTransition( 755 source_map.elements_kind(), target_elements_kind); 756 Map transitioned_map; 757 if (more_general_transition) { 758 MapHandles map_list; 759 map_list.push_back(handle(target_map, isolate_)); 760 transitioned_map = source_map.FindElementsKindTransitionedMap( 761 isolate(), map_list, ConcurrencyMode::kSynchronous); 762 } 763 return transitioned_map == target_map; 764} 765 766void IC::SetCache(Handle<Name> name, Handle<Object> handler) { 767 SetCache(name, MaybeObjectHandle(handler)); 768} 769 770void IC::SetCache(Handle<Name> name, const MaybeObjectHandle& handler) { 771 DCHECK(IsHandler(*handler)); 772 // Currently only load and store ICs support non-code handlers. 773 DCHECK(IsAnyLoad() || IsAnyStore() || IsAnyHas()); 774 switch (state()) { 775 case NO_FEEDBACK: 776 UNREACHABLE(); 777 case UNINITIALIZED: 778 UpdateMonomorphicIC(handler, name); 779 break; 780 case RECOMPUTE_HANDLER: 781 case MONOMORPHIC: 782 if (IsGlobalIC()) { 783 UpdateMonomorphicIC(handler, name); 784 break; 785 } 786 V8_FALLTHROUGH; 787 case POLYMORPHIC: 788 if (UpdatePolymorphicIC(name, handler)) break; 789 if (UpdateMegaDOMIC(handler, name)) break; 790 if (!is_keyed() || state() == RECOMPUTE_HANDLER) { 791 CopyICToMegamorphicCache(name); 792 } 793 V8_FALLTHROUGH; 794 case MEGADOM: 795 ConfigureVectorState(MEGAMORPHIC, name); 796 V8_FALLTHROUGH; 797 case MEGAMORPHIC: 798 UpdateMegamorphicCache(lookup_start_object_map(), name, handler); 799 // Indicate that we've handled this case. 800 vector_set_ = true; 801 break; 802 case GENERIC: 803 UNREACHABLE(); 804 } 805} 806 807void LoadIC::UpdateCaches(LookupIterator* lookup) { 808 Handle<Object> handler; 809 if (lookup->state() == LookupIterator::ACCESS_CHECK) { 810 handler = LoadHandler::LoadSlow(isolate()); 811 } else if (!lookup->IsFound()) { 812 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH); 813 Handle<Smi> smi_handler = LoadHandler::LoadNonExistent(isolate()); 814 handler = LoadHandler::LoadFullChain( 815 isolate(), lookup_start_object_map(), 816 MaybeObjectHandle(isolate()->factory()->null_value()), smi_handler); 817 } else if (IsLoadGlobalIC() && lookup->state() == LookupIterator::JSPROXY) { 818 // If there is proxy just install the slow stub since we need to call the 819 // HasProperty trap for global loads. The ProxyGetProperty builtin doesn't 820 // handle this case. 821 handler = LoadHandler::LoadSlow(isolate()); 822 } else { 823 if (IsLoadGlobalIC()) { 824 if (lookup->TryLookupCachedProperty()) { 825 DCHECK_EQ(LookupIterator::DATA, lookup->state()); 826 } 827 if (lookup->state() == LookupIterator::DATA && 828 lookup->GetReceiver().is_identical_to(lookup->GetHolder<Object>())) { 829 DCHECK(lookup->GetReceiver()->IsJSGlobalObject()); 830 // Now update the cell in the feedback vector. 831 nexus()->ConfigurePropertyCellMode(lookup->GetPropertyCell()); 832 TraceIC("LoadGlobalIC", lookup->name()); 833 return; 834 } 835 } 836 handler = ComputeHandler(lookup); 837 } 838 // Can't use {lookup->name()} because the LookupIterator might be in 839 // "elements" mode for keys that are strings representing integers above 840 // JSArray::kMaxIndex. 841 SetCache(lookup->GetName(), handler); 842 TraceIC("LoadIC", lookup->GetName()); 843} 844 845StubCache* IC::stub_cache() { 846 // HasICs and each of the store own ICs require its own stub cache. 847 // Until we create them, don't allow accessing the load/store stub caches. 848 DCHECK(!IsAnyHas()); 849 DCHECK(!IsAnyDefineOwn()); 850 if (IsAnyLoad()) { 851 return isolate()->load_stub_cache(); 852 } else { 853 DCHECK(IsAnyStore()); 854 return isolate()->store_stub_cache(); 855 } 856} 857 858void IC::UpdateMegamorphicCache(Handle<Map> map, Handle<Name> name, 859 const MaybeObjectHandle& handler) { 860 if (!IsAnyHas() && !IsAnyDefineOwn()) { 861 stub_cache()->Set(*name, *map, *handler); 862 } 863} 864 865namespace { 866 867#if V8_ENABLE_WEBASSEMBLY 868 869inline WasmValueType GetWasmValueType(wasm::ValueType type) { 870#define TYPE_CASE(Name) \ 871 case wasm::k##Name: \ 872 return WasmValueType::k##Name; 873 874 switch (type.kind()) { 875 TYPE_CASE(I8) 876 TYPE_CASE(I16) 877 TYPE_CASE(I32) 878 TYPE_CASE(I64) 879 TYPE_CASE(F32) 880 TYPE_CASE(F64) 881 TYPE_CASE(S128) 882 TYPE_CASE(Ref) 883 TYPE_CASE(OptRef) 884 885 case wasm::kRtt: 886 // Rtt values are not supposed to be made available to JavaScript side. 887 UNREACHABLE(); 888 889 case wasm::kVoid: 890 case wasm::kBottom: 891 UNREACHABLE(); 892 } 893#undef TYPE_CASE 894} 895 896Handle<Smi> MakeLoadWasmStructFieldHandler(Isolate* isolate, 897 Handle<JSReceiver> holder, 898 LookupIterator* lookup) { 899 DCHECK(holder->IsWasmObject(isolate)); 900 WasmValueType type; 901 int field_offset; 902 if (holder->IsWasmArray(isolate)) { 903 // The only named property that WasmArray has is length. 904 DCHECK_EQ(0, lookup->property_details().field_index()); 905 DCHECK_EQ(*isolate->factory()->length_string(), *lookup->name()); 906 type = WasmValueType::kU32; 907 field_offset = WasmArray::kLengthOffset; 908 } else { 909 wasm::StructType* struct_type = Handle<WasmStruct>::cast(holder)->type(); 910 int field_index = lookup->property_details().field_index(); 911 type = GetWasmValueType(struct_type->field(field_index)); 912 field_offset = 913 WasmStruct::kHeaderSize + struct_type->field_offset(field_index); 914 915 const size_t kMaxWasmFieldOffset = 916 WasmStruct::kHeaderSize + wasm::StructType::kMaxFieldOffset; 917 static_assert(kMaxWasmFieldOffset <= LoadHandler::WasmFieldOffsetBits::kMax, 918 "Bigger numbers of struct fields require different approach"); 919 } 920 return LoadHandler::LoadWasmStructField(isolate, type, field_offset); 921} 922 923#endif // V8_ENABLE_WEBASSEMBLY 924 925} // namespace 926 927Handle<Object> IC::CodeHandler(Builtin builtin) { 928 return MakeCodeHandler(isolate(), builtin); 929} 930 931Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) { 932 Handle<Object> receiver = lookup->GetReceiver(); 933 ReadOnlyRoots roots(isolate()); 934 935 Handle<Object> lookup_start_object = lookup->lookup_start_object(); 936 // `in` cannot be called on strings, and will always return true for string 937 // wrapper length and function prototypes. The latter two cases are given 938 // LoadHandler::LoadNativeDataProperty below. 939 if (!IsAnyHas() && !lookup->IsElement()) { 940 if (lookup_start_object->IsString() && 941 *lookup->name() == roots.length_string()) { 942 TRACE_HANDLER_STATS(isolate(), LoadIC_StringLength); 943 return CodeHandler(Builtin::kLoadIC_StringLength); 944 } 945 946 if (lookup_start_object->IsStringWrapper() && 947 *lookup->name() == roots.length_string()) { 948 TRACE_HANDLER_STATS(isolate(), LoadIC_StringWrapperLength); 949 return CodeHandler(Builtin::kLoadIC_StringWrapperLength); 950 } 951 952 // Use specialized code for getting prototype of functions. 953 if (lookup_start_object->IsJSFunction() && 954 *lookup->name() == roots.prototype_string() && 955 !JSFunction::cast(*lookup_start_object) 956 .PrototypeRequiresRuntimeLookup()) { 957 TRACE_HANDLER_STATS(isolate(), LoadIC_FunctionPrototypeStub); 958 return CodeHandler(Builtin::kLoadIC_FunctionPrototype); 959 } 960 } 961 962 Handle<Map> map = lookup_start_object_map(); 963 bool holder_is_lookup_start_object = 964 lookup_start_object.is_identical_to(lookup->GetHolder<JSReceiver>()); 965 966 switch (lookup->state()) { 967 case LookupIterator::INTERCEPTOR: { 968 Handle<JSObject> holder = lookup->GetHolder<JSObject>(); 969 Handle<Smi> smi_handler = LoadHandler::LoadInterceptor(isolate()); 970 971 if (holder->GetNamedInterceptor().non_masking()) { 972 MaybeObjectHandle holder_ref(isolate()->factory()->null_value()); 973 if (!holder_is_lookup_start_object || IsLoadGlobalIC()) { 974 holder_ref = MaybeObjectHandle::Weak(holder); 975 } 976 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonMaskingInterceptorDH); 977 return LoadHandler::LoadFullChain(isolate(), map, holder_ref, 978 smi_handler); 979 } 980 981 if (holder_is_lookup_start_object) { 982 DCHECK(map->has_named_interceptor()); 983 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptorDH); 984 return smi_handler; 985 } 986 987 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadInterceptorFromPrototypeDH); 988 return LoadHandler::LoadFromPrototype(isolate(), map, holder, 989 smi_handler); 990 } 991 992 case LookupIterator::ACCESSOR: { 993 Handle<JSObject> holder = lookup->GetHolder<JSObject>(); 994 // Use simple field loads for some well-known callback properties. 995 // The method will only return true for absolute truths based on the 996 // lookup start object maps. 997 FieldIndex field_index; 998 if (Accessors::IsJSObjectFieldAccessor(isolate(), map, lookup->name(), 999 &field_index)) { 1000 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldDH); 1001 return LoadHandler::LoadField(isolate(), field_index); 1002 } 1003 if (holder->IsJSModuleNamespace()) { 1004 Handle<ObjectHashTable> exports( 1005 Handle<JSModuleNamespace>::cast(holder)->module().exports(), 1006 isolate()); 1007 InternalIndex entry = 1008 exports->FindEntry(isolate(), roots, lookup->name(), 1009 Smi::ToInt(lookup->name()->GetHash())); 1010 // We found the accessor, so the entry must exist. 1011 DCHECK(entry.is_found()); 1012 int value_index = ObjectHashTable::EntryToValueIndex(entry); 1013 Handle<Smi> smi_handler = 1014 LoadHandler::LoadModuleExport(isolate(), value_index); 1015 if (holder_is_lookup_start_object) { 1016 return smi_handler; 1017 } 1018 return LoadHandler::LoadFromPrototype(isolate(), map, holder, 1019 smi_handler); 1020 } 1021 1022 Handle<Object> accessors = lookup->GetAccessors(); 1023 if (accessors->IsAccessorPair()) { 1024 Handle<AccessorPair> accessor_pair = 1025 Handle<AccessorPair>::cast(accessors); 1026 if (lookup->TryLookupCachedProperty(accessor_pair)) { 1027 DCHECK_EQ(LookupIterator::DATA, lookup->state()); 1028 return ComputeHandler(lookup); 1029 } 1030 1031 Handle<Object> getter(accessor_pair->getter(), isolate()); 1032 if (!getter->IsJSFunction() && !getter->IsFunctionTemplateInfo()) { 1033 // TODO(jgruber): Update counter name. 1034 TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); 1035 return LoadHandler::LoadSlow(isolate()); 1036 } 1037 set_accessor(getter); 1038 1039 if ((getter->IsFunctionTemplateInfo() && 1040 FunctionTemplateInfo::cast(*getter).BreakAtEntry()) || 1041 (getter->IsJSFunction() && 1042 JSFunction::cast(*getter).shared().BreakAtEntry())) { 1043 // Do not install an IC if the api function has a breakpoint. 1044 TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); 1045 return LoadHandler::LoadSlow(isolate()); 1046 } 1047 1048 Handle<Smi> smi_handler; 1049 1050 CallOptimization call_optimization(isolate(), getter); 1051 if (call_optimization.is_simple_api_call()) { 1052 CallOptimization::HolderLookup holder_lookup; 1053 Handle<JSObject> api_holder = 1054 call_optimization.LookupHolderOfExpectedType(isolate(), map, 1055 &holder_lookup); 1056 1057 if (!call_optimization.IsCompatibleReceiverMap(api_holder, holder, 1058 holder_lookup) || 1059 !holder->HasFastProperties()) { 1060 TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); 1061 return LoadHandler::LoadSlow(isolate()); 1062 } 1063 1064 smi_handler = LoadHandler::LoadApiGetter( 1065 isolate(), holder_lookup == CallOptimization::kHolderIsReceiver); 1066 1067 Handle<Context> context( 1068 call_optimization.GetAccessorContext(holder->map()), isolate()); 1069 1070 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadApiGetterFromPrototypeDH); 1071 return LoadHandler::LoadFromPrototype( 1072 isolate(), map, holder, smi_handler, 1073 MaybeObjectHandle::Weak(call_optimization.api_call_info()), 1074 MaybeObjectHandle::Weak(context)); 1075 } 1076 1077 if (holder->HasFastProperties()) { 1078 smi_handler = 1079 LoadHandler::LoadAccessor(isolate(), lookup->GetAccessorIndex()); 1080 1081 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadAccessorDH); 1082 if (holder_is_lookup_start_object) return smi_handler; 1083 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadAccessorFromPrototypeDH); 1084 } else if (holder->IsJSGlobalObject()) { 1085 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadGlobalFromPrototypeDH); 1086 smi_handler = LoadHandler::LoadGlobal(isolate()); 1087 return LoadHandler::LoadFromPrototype( 1088 isolate(), map, holder, smi_handler, 1089 MaybeObjectHandle::Weak(lookup->GetPropertyCell())); 1090 } else { 1091 smi_handler = LoadHandler::LoadNormal(isolate()); 1092 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalDH); 1093 if (holder_is_lookup_start_object) return smi_handler; 1094 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalFromPrototypeDH); 1095 } 1096 1097 return LoadHandler::LoadFromPrototype(isolate(), map, holder, 1098 smi_handler); 1099 } 1100 1101 Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors); 1102 1103 if (info->replace_on_access()) { 1104 set_slow_stub_reason( 1105 "getter needs to be reconfigured to data property"); 1106 TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); 1107 return LoadHandler::LoadSlow(isolate()); 1108 } 1109 1110 if (v8::ToCData<Address>(info->getter()) == kNullAddress || 1111 !AccessorInfo::IsCompatibleReceiverMap(info, map) || 1112 !holder->HasFastProperties() || 1113 (info->is_sloppy() && !receiver->IsJSReceiver())) { 1114 TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); 1115 return LoadHandler::LoadSlow(isolate()); 1116 } 1117 1118 Handle<Smi> smi_handler = LoadHandler::LoadNativeDataProperty( 1119 isolate(), lookup->GetAccessorIndex()); 1120 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNativeDataPropertyDH); 1121 if (holder_is_lookup_start_object) return smi_handler; 1122 TRACE_HANDLER_STATS(isolate(), 1123 LoadIC_LoadNativeDataPropertyFromPrototypeDH); 1124 return LoadHandler::LoadFromPrototype(isolate(), map, holder, 1125 smi_handler); 1126 } 1127 1128 case LookupIterator::DATA: { 1129 Handle<JSReceiver> holder = lookup->GetHolder<JSReceiver>(); 1130 DCHECK_EQ(PropertyKind::kData, lookup->property_details().kind()); 1131 Handle<Smi> smi_handler; 1132 if (lookup->is_dictionary_holder()) { 1133 if (holder->IsJSGlobalObject(isolate())) { 1134 // TODO(verwaest): Also supporting the global object as receiver is a 1135 // workaround for code that leaks the global object. 1136 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadGlobalDH); 1137 smi_handler = LoadHandler::LoadGlobal(isolate()); 1138 return LoadHandler::LoadFromPrototype( 1139 isolate(), map, holder, smi_handler, 1140 MaybeObjectHandle::Weak(lookup->GetPropertyCell())); 1141 } 1142 smi_handler = LoadHandler::LoadNormal(isolate()); 1143 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalDH); 1144 if (holder_is_lookup_start_object) return smi_handler; 1145 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNormalFromPrototypeDH); 1146 } else if (lookup->IsElement(*holder)) { 1147#if V8_ENABLE_WEBASSEMBLY 1148 if (holder_is_lookup_start_object && holder->IsWasmStruct()) { 1149 // TODO(ishell): Consider supporting indexed access to WasmStruct 1150 // fields. 1151 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH); 1152 return LoadHandler::LoadNonExistent(isolate()); 1153 } 1154#endif // V8_ENABLE_WEBASSEMBLY 1155 TRACE_HANDLER_STATS(isolate(), LoadIC_SlowStub); 1156 return LoadHandler::LoadSlow(isolate()); 1157 } else { 1158 DCHECK_EQ(PropertyLocation::kField, 1159 lookup->property_details().location()); 1160#if V8_ENABLE_WEBASSEMBLY 1161 if (V8_UNLIKELY(holder->IsWasmObject(isolate()))) { 1162 smi_handler = 1163 MakeLoadWasmStructFieldHandler(isolate(), holder, lookup); 1164 } else // NOLINT(readability/braces) 1165#endif // V8_ENABLE_WEBASSEMBLY 1166 { 1167 DCHECK(holder->IsJSObject(isolate())); 1168 FieldIndex field = lookup->GetFieldIndex(); 1169 smi_handler = LoadHandler::LoadField(isolate(), field); 1170 } 1171 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldDH); 1172 if (holder_is_lookup_start_object) return smi_handler; 1173 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadFieldFromPrototypeDH); 1174 } 1175 if (lookup->constness() == PropertyConstness::kConst && 1176 !holder_is_lookup_start_object) { 1177 DCHECK_IMPLIES(!V8_DICT_PROPERTY_CONST_TRACKING_BOOL, 1178 !lookup->is_dictionary_holder()); 1179 1180 Handle<Object> value = lookup->GetDataValue(); 1181 1182 if (value->IsThinString()) { 1183 value = handle(ThinString::cast(*value).actual(), isolate()); 1184 } 1185 1186 // Non internalized strings could turn into thin/cons strings 1187 // when internalized. Weak references to thin/cons strings are 1188 // not supported in the GC. If concurrent marking is running 1189 // and the thin/cons string is marked but the actual string is 1190 // not, then the weak reference could be missed. 1191 if (!value->IsString() || 1192 (value->IsString() && value->IsInternalizedString())) { 1193 MaybeObjectHandle weak_value = 1194 value->IsSmi() ? MaybeObjectHandle(*value, isolate()) 1195 : MaybeObjectHandle::Weak(*value, isolate()); 1196 1197 smi_handler = LoadHandler::LoadConstantFromPrototype(isolate()); 1198 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadConstantFromPrototypeDH); 1199 return LoadHandler::LoadFromPrototype(isolate(), map, holder, 1200 smi_handler, weak_value); 1201 } 1202 } 1203 return LoadHandler::LoadFromPrototype(isolate(), map, holder, 1204 smi_handler); 1205 } 1206 case LookupIterator::INTEGER_INDEXED_EXOTIC: 1207 TRACE_HANDLER_STATS(isolate(), LoadIC_LoadIntegerIndexedExoticDH); 1208 return LoadHandler::LoadNonExistent(isolate()); 1209 1210 case LookupIterator::JSPROXY: { 1211 Handle<Smi> smi_handler = LoadHandler::LoadProxy(isolate()); 1212 if (holder_is_lookup_start_object) return smi_handler; 1213 1214 Handle<JSProxy> holder_proxy = lookup->GetHolder<JSProxy>(); 1215 return LoadHandler::LoadFromPrototype(isolate(), map, holder_proxy, 1216 smi_handler); 1217 } 1218 case LookupIterator::ACCESS_CHECK: 1219 case LookupIterator::NOT_FOUND: 1220 case LookupIterator::TRANSITION: 1221 UNREACHABLE(); 1222 } 1223 1224 return Handle<Code>::null(); 1225} 1226 1227bool KeyedLoadIC::CanChangeToAllowOutOfBounds(Handle<Map> receiver_map) { 1228 const MaybeObjectHandle& handler = nexus()->FindHandlerForMap(receiver_map); 1229 if (handler.is_null()) return false; 1230 return LoadHandler::GetKeyedAccessLoadMode(*handler) == STANDARD_LOAD; 1231} 1232 1233void KeyedLoadIC::UpdateLoadElement(Handle<HeapObject> receiver, 1234 KeyedAccessLoadMode load_mode) { 1235 Handle<Map> receiver_map(receiver->map(), isolate()); 1236 DCHECK(receiver_map->instance_type() != 1237 JS_PRIMITIVE_WRAPPER_TYPE); // Checked by caller. 1238 MapHandles target_receiver_maps; 1239 TargetMaps(&target_receiver_maps); 1240 1241 if (target_receiver_maps.empty()) { 1242 Handle<Object> handler = LoadElementHandler(receiver_map, load_mode); 1243 return ConfigureVectorState(Handle<Name>(), receiver_map, handler); 1244 } 1245 1246 for (Handle<Map> map : target_receiver_maps) { 1247 if (map.is_null()) continue; 1248 if (map->instance_type() == JS_PRIMITIVE_WRAPPER_TYPE) { 1249 set_slow_stub_reason("JSPrimitiveWrapper"); 1250 return; 1251 } 1252 if (map->instance_type() == JS_PROXY_TYPE) { 1253 set_slow_stub_reason("JSProxy"); 1254 return; 1255 } 1256 } 1257 1258 // The first time a receiver is seen that is a transitioned version of the 1259 // previous monomorphic receiver type, assume the new ElementsKind is the 1260 // monomorphic type. This benefits global arrays that only transition 1261 // once, and all call sites accessing them are faster if they remain 1262 // monomorphic. If this optimistic assumption is not true, the IC will 1263 // miss again and it will become polymorphic and support both the 1264 // untransitioned and transitioned maps. 1265 if (state() == MONOMORPHIC) { 1266 if ((receiver->IsJSObject() && 1267 IsMoreGeneralElementsKindTransition( 1268 target_receiver_maps.at(0)->elements_kind(), 1269 Handle<JSObject>::cast(receiver)->GetElementsKind())) 1270#ifdef V8_ENABLE_WEBASSEMBLY 1271 || receiver->IsWasmObject() 1272#endif 1273 ) { 1274 Handle<Object> handler = LoadElementHandler(receiver_map, load_mode); 1275 return ConfigureVectorState(Handle<Name>(), receiver_map, handler); 1276 } 1277 } 1278 1279 DCHECK(state() != GENERIC); 1280 1281 // Determine the list of receiver maps that this call site has seen, 1282 // adding the map that was just encountered. 1283 if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map)) { 1284 // If the {receiver_map} previously had a handler that didn't handle 1285 // out-of-bounds access, but can generally handle it, we can just go 1286 // on and update the handler appropriately below. 1287 if (load_mode != LOAD_IGNORE_OUT_OF_BOUNDS || 1288 !CanChangeToAllowOutOfBounds(receiver_map)) { 1289 // If the miss wasn't due to an unseen map, a polymorphic stub 1290 // won't help, use the generic stub. 1291 set_slow_stub_reason("same map added twice"); 1292 return; 1293 } 1294 } 1295 1296 // If the maximum number of receiver maps has been exceeded, use the generic 1297 // version of the IC. 1298 if (static_cast<int>(target_receiver_maps.size()) > 1299 FLAG_max_valid_polymorphic_map_count) { 1300 set_slow_stub_reason("max polymorph exceeded"); 1301 return; 1302 } 1303 1304 MaybeObjectHandles handlers; 1305 handlers.reserve(target_receiver_maps.size()); 1306 LoadElementPolymorphicHandlers(&target_receiver_maps, &handlers, load_mode); 1307 DCHECK_LE(1, target_receiver_maps.size()); 1308 if (target_receiver_maps.size() == 1) { 1309 ConfigureVectorState(Handle<Name>(), target_receiver_maps[0], handlers[0]); 1310 } else { 1311 ConfigureVectorState(Handle<Name>(), target_receiver_maps, &handlers); 1312 } 1313} 1314 1315namespace { 1316 1317bool AllowConvertHoleElementToUndefined(Isolate* isolate, 1318 Handle<Map> receiver_map) { 1319 if (receiver_map->IsJSTypedArrayMap()) { 1320 // For JSTypedArray we never lookup elements in the prototype chain. 1321 return true; 1322 } 1323 1324 // For other {receiver}s we need to check the "no elements" protector. 1325 if (Protectors::IsNoElementsIntact(isolate)) { 1326 if (receiver_map->IsStringMap()) { 1327 return true; 1328 } 1329 if (receiver_map->IsJSObjectMap()) { 1330 // For other JSObjects (including JSArrays) we can only continue if 1331 // the {receiver}s prototype is either the initial Object.prototype 1332 // or the initial Array.prototype, which are both guarded by the 1333 // "no elements" protector checked above. 1334 Handle<Object> receiver_prototype(receiver_map->prototype(), isolate); 1335 1336 if (isolate->IsInAnyContext(*receiver_prototype, 1337 Context::INITIAL_ARRAY_PROTOTYPE_INDEX) || 1338 isolate->IsInAnyContext(*receiver_prototype, 1339 Context::INITIAL_OBJECT_PROTOTYPE_INDEX)) { 1340 return true; 1341 } 1342 } 1343 } 1344 1345 return false; 1346} 1347} // namespace 1348 1349Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map, 1350 KeyedAccessLoadMode load_mode) { 1351 // Has a getter interceptor, or is any has and has a query interceptor. 1352 if (receiver_map->has_indexed_interceptor() && 1353 (!receiver_map->GetIndexedInterceptor().getter().IsUndefined(isolate()) || 1354 (IsAnyHas() && 1355 !receiver_map->GetIndexedInterceptor().query().IsUndefined( 1356 isolate()))) && 1357 !receiver_map->GetIndexedInterceptor().non_masking()) { 1358 // TODO(jgruber): Update counter name. 1359 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedInterceptorStub); 1360 return IsAnyHas() ? CodeHandler(Builtin::kHasIndexedInterceptorIC) 1361 : CodeHandler(Builtin::kLoadIndexedInterceptorIC); 1362 } 1363 1364 InstanceType instance_type = receiver_map->instance_type(); 1365 if (instance_type < FIRST_NONSTRING_TYPE) { 1366 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadIndexedStringDH); 1367 if (IsAnyHas()) return LoadHandler::LoadSlow(isolate()); 1368 return LoadHandler::LoadIndexedString(isolate(), load_mode); 1369 } 1370 if (instance_type < FIRST_JS_RECEIVER_TYPE) { 1371 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub); 1372 return LoadHandler::LoadSlow(isolate()); 1373 } 1374 if (instance_type == JS_PROXY_TYPE) { 1375 return LoadHandler::LoadProxy(isolate()); 1376 } 1377#if V8_ENABLE_WEBASSEMBLY 1378 if (InstanceTypeChecker::IsWasmObject(instance_type)) { 1379 // TODO(jgruber): Update counter name. 1380 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub); 1381 return LoadHandler::LoadSlow(isolate()); 1382 } 1383#endif // V8_ENABLE_WEBASSEMBLY 1384 1385 ElementsKind elements_kind = receiver_map->elements_kind(); 1386 if (IsSloppyArgumentsElementsKind(elements_kind)) { 1387 // TODO(jgruber): Update counter name. 1388 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_KeyedLoadSloppyArgumentsStub); 1389 return IsAnyHas() ? CodeHandler(Builtin::kKeyedHasIC_SloppyArguments) 1390 : CodeHandler(Builtin::kKeyedLoadIC_SloppyArguments); 1391 } 1392 bool is_js_array = instance_type == JS_ARRAY_TYPE; 1393 if (elements_kind == DICTIONARY_ELEMENTS) { 1394 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadElementDH); 1395 return LoadHandler::LoadElement(isolate(), elements_kind, false, 1396 is_js_array, load_mode); 1397 } 1398 DCHECK(IsFastElementsKind(elements_kind) || 1399 IsAnyNonextensibleElementsKind(elements_kind) || 1400 IsTypedArrayOrRabGsabTypedArrayElementsKind(elements_kind)); 1401 bool convert_hole_to_undefined = 1402 (elements_kind == HOLEY_SMI_ELEMENTS || 1403 elements_kind == HOLEY_ELEMENTS) && 1404 AllowConvertHoleElementToUndefined(isolate(), receiver_map); 1405 TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_LoadElementDH); 1406 return LoadHandler::LoadElement(isolate(), elements_kind, 1407 convert_hole_to_undefined, is_js_array, 1408 load_mode); 1409} 1410 1411void KeyedLoadIC::LoadElementPolymorphicHandlers( 1412 MapHandles* receiver_maps, MaybeObjectHandles* handlers, 1413 KeyedAccessLoadMode load_mode) { 1414 // Filter out deprecated maps to ensure their instances get migrated. 1415 receiver_maps->erase( 1416 std::remove_if( 1417 receiver_maps->begin(), receiver_maps->end(), 1418 [](const Handle<Map>& map) { return map->is_deprecated(); }), 1419 receiver_maps->end()); 1420 1421 for (Handle<Map> receiver_map : *receiver_maps) { 1422 // Mark all stable receiver maps that have elements kind transition map 1423 // among receiver_maps as unstable because the optimizing compilers may 1424 // generate an elements kind transition for this kind of receivers. 1425 if (receiver_map->is_stable()) { 1426 Map tmap = receiver_map->FindElementsKindTransitionedMap( 1427 isolate(), *receiver_maps, ConcurrencyMode::kSynchronous); 1428 if (!tmap.is_null()) { 1429 receiver_map->NotifyLeafMapLayoutChange(isolate()); 1430 } 1431 } 1432 handlers->push_back( 1433 MaybeObjectHandle(LoadElementHandler(receiver_map, load_mode))); 1434 } 1435} 1436 1437namespace { 1438 1439enum KeyType { kIntPtr, kName, kBailout }; 1440 1441// The cases where kIntPtr is returned must match what 1442// CodeStubAssembler::TryToIntptr can handle! 1443KeyType TryConvertKey(Handle<Object> key, Isolate* isolate, intptr_t* index_out, 1444 Handle<Name>* name_out) { 1445 if (key->IsSmi()) { 1446 *index_out = Smi::ToInt(*key); 1447 return kIntPtr; 1448 } 1449 if (key->IsHeapNumber()) { 1450 double num = HeapNumber::cast(*key).value(); 1451 if (!(num >= -kMaxSafeInteger)) return kBailout; 1452 if (num > kMaxSafeInteger) return kBailout; 1453 *index_out = static_cast<intptr_t>(num); 1454 if (*index_out != num) return kBailout; 1455 return kIntPtr; 1456 } 1457 if (key->IsString()) { 1458 key = isolate->factory()->InternalizeString(Handle<String>::cast(key)); 1459 uint32_t maybe_array_index; 1460 if (String::cast(*key).AsArrayIndex(&maybe_array_index)) { 1461 if (maybe_array_index <= INT_MAX) { 1462 *index_out = static_cast<intptr_t>(maybe_array_index); 1463 return kIntPtr; 1464 } 1465 // {key} is a string representation of an array index beyond the range 1466 // that the IC could handle. Don't try to take the named-property path. 1467 return kBailout; 1468 } 1469 *name_out = Handle<String>::cast(key); 1470 return kName; 1471 } 1472 if (key->IsSymbol()) { 1473 *name_out = Handle<Symbol>::cast(key); 1474 return kName; 1475 } 1476 return kBailout; 1477} 1478 1479bool IntPtrKeyToSize(intptr_t index, Handle<HeapObject> receiver, size_t* out) { 1480 if (index < 0) { 1481 if (receiver->IsJSTypedArray()) { 1482 // For JSTypedArray receivers, we can support negative keys, which we 1483 // just map to a very large value. This is valid because all OOB accesses 1484 // (negative or positive) are handled the same way, and size_t::max is 1485 // guaranteed to be an OOB access. 1486 *out = std::numeric_limits<size_t>::max(); 1487 return true; 1488 } 1489 return false; 1490 } 1491#if V8_HOST_ARCH_64_BIT 1492 if (index > JSObject::kMaxElementIndex && !receiver->IsJSTypedArray()) { 1493 return false; 1494 } 1495#else 1496 // On 32-bit platforms, any intptr_t is less than kMaxElementIndex. 1497 STATIC_ASSERT( 1498 static_cast<double>(std::numeric_limits<decltype(index)>::max()) <= 1499 static_cast<double>(JSObject::kMaxElementIndex)); 1500#endif 1501 *out = static_cast<size_t>(index); 1502 return true; 1503} 1504 1505bool CanCache(Handle<Object> receiver, InlineCacheState state) { 1506 if (!FLAG_use_ic || state == NO_FEEDBACK) return false; 1507 if (!receiver->IsJSReceiver() && !receiver->IsString()) return false; 1508 return !receiver->IsAccessCheckNeeded() && !receiver->IsJSPrimitiveWrapper(); 1509} 1510 1511bool IsOutOfBoundsAccess(Handle<Object> receiver, size_t index) { 1512 size_t length; 1513 if (receiver->IsJSArray()) { 1514 length = JSArray::cast(*receiver).length().Number(); 1515 } else if (receiver->IsJSTypedArray()) { 1516 length = JSTypedArray::cast(*receiver).GetLength(); 1517 } else if (receiver->IsJSObject()) { 1518 length = JSObject::cast(*receiver).elements().length(); 1519 } else if (receiver->IsString()) { 1520 length = String::cast(*receiver).length(); 1521 } else { 1522 return false; 1523 } 1524 return index >= length; 1525} 1526 1527KeyedAccessLoadMode GetLoadMode(Isolate* isolate, Handle<Object> receiver, 1528 size_t index) { 1529 if (IsOutOfBoundsAccess(receiver, index)) { 1530 DCHECK(receiver->IsHeapObject()); 1531 Handle<Map> receiver_map(Handle<HeapObject>::cast(receiver)->map(), 1532 isolate); 1533 if (AllowConvertHoleElementToUndefined(isolate, receiver_map)) { 1534 return LOAD_IGNORE_OUT_OF_BOUNDS; 1535 } 1536 } 1537 return STANDARD_LOAD; 1538} 1539 1540} // namespace 1541 1542MaybeHandle<Object> KeyedLoadIC::RuntimeLoad(Handle<Object> object, 1543 Handle<Object> key) { 1544 Handle<Object> result; 1545 1546 if (IsKeyedLoadIC()) { 1547 ASSIGN_RETURN_ON_EXCEPTION( 1548 isolate(), result, Runtime::GetObjectProperty(isolate(), object, key), 1549 Object); 1550 } else { 1551 DCHECK(IsKeyedHasIC()); 1552 ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, 1553 Runtime::HasProperty(isolate(), object, key), 1554 Object); 1555 } 1556 return result; 1557} 1558 1559MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object, 1560 Handle<Object> key) { 1561 if (MigrateDeprecated(isolate(), object)) { 1562 return RuntimeLoad(object, key); 1563 } 1564 1565 Handle<Object> load_handle; 1566 1567 intptr_t maybe_index; 1568 size_t index; 1569 Handle<Name> maybe_name; 1570 KeyType key_type = TryConvertKey(key, isolate(), &maybe_index, &maybe_name); 1571 1572 if (key_type == kName) { 1573 ASSIGN_RETURN_ON_EXCEPTION(isolate(), load_handle, 1574 LoadIC::Load(object, maybe_name), Object); 1575 } else if (key_type == kIntPtr && CanCache(object, state()) && 1576 IntPtrKeyToSize(maybe_index, Handle<HeapObject>::cast(object), 1577 &index)) { 1578 KeyedAccessLoadMode load_mode = GetLoadMode(isolate(), object, index); 1579 UpdateLoadElement(Handle<HeapObject>::cast(object), load_mode); 1580 if (is_vector_set()) { 1581 TraceIC("LoadIC", key); 1582 } 1583 } 1584 1585 if (vector_needs_update()) { 1586 ConfigureVectorState(MEGAMORPHIC, key); 1587 TraceIC("LoadIC", key); 1588 } 1589 1590 if (!load_handle.is_null()) return load_handle; 1591 1592 return RuntimeLoad(object, key); 1593} 1594 1595bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value, 1596 StoreOrigin store_origin) { 1597 // Disable ICs for non-JSObjects for now. 1598 Handle<Object> object = it->GetReceiver(); 1599 if (object->IsJSProxy()) return true; 1600 if (!object->IsJSObject()) return false; 1601 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 1602 DCHECK(!receiver->map().is_deprecated()); 1603 1604 if (it->state() != LookupIterator::TRANSITION) { 1605 for (; it->IsFound(); it->Next()) { 1606 switch (it->state()) { 1607 case LookupIterator::NOT_FOUND: 1608 case LookupIterator::TRANSITION: 1609 UNREACHABLE(); 1610 case LookupIterator::JSPROXY: 1611 return true; 1612 case LookupIterator::INTERCEPTOR: { 1613 Handle<JSObject> holder = it->GetHolder<JSObject>(); 1614 InterceptorInfo info = holder->GetNamedInterceptor(); 1615 if (it->HolderIsReceiverOrHiddenPrototype() || 1616 !info.getter().IsUndefined(isolate()) || 1617 !info.query().IsUndefined(isolate())) { 1618 return true; 1619 } 1620 break; 1621 } 1622 case LookupIterator::ACCESS_CHECK: 1623 if (it->GetHolder<JSObject>()->IsAccessCheckNeeded()) return false; 1624 break; 1625 case LookupIterator::ACCESSOR: 1626 return !it->IsReadOnly(); 1627 case LookupIterator::INTEGER_INDEXED_EXOTIC: 1628 return false; 1629 case LookupIterator::DATA: { 1630 if (it->IsReadOnly()) return false; 1631 Handle<JSObject> holder = it->GetHolder<JSObject>(); 1632 if (receiver.is_identical_to(holder)) { 1633 it->PrepareForDataProperty(value); 1634 // The previous receiver map might just have been deprecated, 1635 // so reload it. 1636 update_lookup_start_object_map(receiver); 1637 return true; 1638 } 1639 1640 // Receiver != holder. 1641 if (receiver->IsJSGlobalProxy()) { 1642 PrototypeIterator iter(isolate(), receiver); 1643 return it->GetHolder<Object>().is_identical_to( 1644 PrototypeIterator::GetCurrent(iter)); 1645 } 1646 1647 if (it->HolderIsReceiverOrHiddenPrototype()) return false; 1648 1649 if (it->ExtendingNonExtensible(receiver)) return false; 1650 it->PrepareTransitionToDataProperty(receiver, value, NONE, 1651 store_origin); 1652 return it->IsCacheableTransition(); 1653 } 1654 } 1655 } 1656 } 1657 1658 // If we are in StoreGlobal then check if we should throw on non-existent 1659 // properties. 1660 if (IsStoreGlobalIC() && 1661 (GetShouldThrow(it->isolate(), Nothing<ShouldThrow>()) == 1662 ShouldThrow::kThrowOnError)) { 1663 // ICs typically does the store in two steps: prepare receiver for the 1664 // transition followed by the actual store. For global objects we create a 1665 // property cell when preparing for transition and install this cell in the 1666 // handler. In strict mode, we throw and never initialize this property 1667 // cell. The IC handler assumes that the property cell it is holding is for 1668 // a property that is existing. This case violates this assumption. If we 1669 // happen to invalidate this property cell later, it leads to incorrect 1670 // behaviour. For now just use a slow stub and don't install the property 1671 // cell for these cases. Hopefully these cases are not frequent enough to 1672 // impact performance. 1673 // 1674 // TODO(mythria): If we find this to be happening often, we could install a 1675 // new kind of handler for non-existent properties. These handlers can then 1676 // miss to runtime if the value is not hole (i.e. cell got invalidated) and 1677 // handle these stores correctly. 1678 return false; 1679 } 1680 receiver = it->GetStoreTarget<JSObject>(); 1681 if (it->ExtendingNonExtensible(receiver)) return false; 1682 it->PrepareTransitionToDataProperty(receiver, value, NONE, store_origin); 1683 return it->IsCacheableTransition(); 1684} 1685 1686MaybeHandle<Object> StoreGlobalIC::Store(Handle<Name> name, 1687 Handle<Object> value) { 1688 DCHECK(name->IsString()); 1689 1690 // Look up in script context table. 1691 Handle<String> str_name = Handle<String>::cast(name); 1692 Handle<JSGlobalObject> global = isolate()->global_object(); 1693 Handle<ScriptContextTable> script_contexts( 1694 global->native_context().script_context_table(), isolate()); 1695 1696 VariableLookupResult lookup_result; 1697 if (script_contexts->Lookup(str_name, &lookup_result)) { 1698 Handle<Context> script_context = ScriptContextTable::GetContext( 1699 isolate(), script_contexts, lookup_result.context_index); 1700 if (lookup_result.mode == VariableMode::kConst) { 1701 return TypeError(MessageTemplate::kConstAssign, global, name); 1702 } 1703 1704 Handle<Object> previous_value(script_context->get(lookup_result.slot_index), 1705 isolate()); 1706 1707 if (previous_value->IsTheHole(isolate())) { 1708 // Do not install stubs and stay pre-monomorphic for 1709 // uninitialized accesses. 1710 THROW_NEW_ERROR( 1711 isolate(), 1712 NewReferenceError(MessageTemplate::kAccessedUninitializedVariable, 1713 name), 1714 Object); 1715 } 1716 1717 bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic; 1718 if (use_ic) { 1719 if (nexus()->ConfigureLexicalVarMode( 1720 lookup_result.context_index, lookup_result.slot_index, 1721 lookup_result.mode == VariableMode::kConst)) { 1722 TRACE_HANDLER_STATS(isolate(), StoreGlobalIC_StoreScriptContextField); 1723 } else { 1724 // Given combination of indices can't be encoded, so use slow stub. 1725 TRACE_HANDLER_STATS(isolate(), StoreGlobalIC_SlowStub); 1726 SetCache(name, StoreHandler::StoreSlow(isolate())); 1727 } 1728 TraceIC("StoreGlobalIC", name); 1729 } else if (state() == NO_FEEDBACK) { 1730 TraceIC("StoreGlobalIC", name); 1731 } 1732 script_context->set(lookup_result.slot_index, *value); 1733 return value; 1734 } 1735 1736 return StoreIC::Store(global, name, value); 1737} 1738 1739namespace { 1740Maybe<bool> DefineOwnDataProperty(LookupIterator* it, 1741 LookupIterator::State original_state, 1742 Handle<Object> value, 1743 Maybe<ShouldThrow> should_throw, 1744 StoreOrigin store_origin) { 1745 // It should not be possible to call DefineOwnDataProperty in a 1746 // contextual store (indicated by IsJSGlobalObject()). 1747 DCHECK(!it->GetReceiver()->IsJSGlobalObject(it->isolate())); 1748 1749 // Handle special cases that can't be handled by 1750 // DefineOwnPropertyIgnoreAttributes first. 1751 switch (it->state()) { 1752 case LookupIterator::JSPROXY: { 1753 PropertyDescriptor new_desc; 1754 new_desc.set_value(value); 1755 new_desc.set_writable(true); 1756 new_desc.set_enumerable(true); 1757 new_desc.set_configurable(true); 1758 DCHECK_EQ(original_state, LookupIterator::JSPROXY); 1759 // TODO(joyee): this will start the lookup again. Ideally we should 1760 // implement something that reuses the existing LookupIterator. 1761 return JSProxy::DefineOwnProperty(it->isolate(), it->GetHolder<JSProxy>(), 1762 it->GetName(), &new_desc, should_throw); 1763 } 1764 // When lazy feedback is disabled, the original state could be different 1765 // while the object is already prepared for TRANSITION. 1766 case LookupIterator::TRANSITION: { 1767 switch (original_state) { 1768 case LookupIterator::JSPROXY: 1769 case LookupIterator::TRANSITION: 1770 case LookupIterator::DATA: 1771 case LookupIterator::INTERCEPTOR: 1772 case LookupIterator::ACCESSOR: 1773 case LookupIterator::INTEGER_INDEXED_EXOTIC: 1774 UNREACHABLE(); 1775 case LookupIterator::ACCESS_CHECK: { 1776 DCHECK(!it->GetHolder<JSObject>()->IsAccessCheckNeeded()); 1777 V8_FALLTHROUGH; 1778 } 1779 case LookupIterator::NOT_FOUND: 1780 return Object::AddDataProperty(it, value, NONE, 1781 Nothing<ShouldThrow>(), store_origin, 1782 EnforceDefineSemantics::kDefine); 1783 } 1784 } 1785 case LookupIterator::ACCESS_CHECK: 1786 case LookupIterator::NOT_FOUND: 1787 case LookupIterator::DATA: 1788 case LookupIterator::ACCESSOR: 1789 case LookupIterator::INTERCEPTOR: 1790 case LookupIterator::INTEGER_INDEXED_EXOTIC: 1791 break; 1792 } 1793 1794 // We need to restart to handle interceptors properly. 1795 it->Restart(); 1796 1797 return JSObject::DefineOwnPropertyIgnoreAttributes( 1798 it, value, NONE, should_throw, JSObject::DONT_FORCE_FIELD, 1799 EnforceDefineSemantics::kDefine, store_origin); 1800} 1801} // namespace 1802 1803MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name, 1804 Handle<Object> value, 1805 StoreOrigin store_origin) { 1806 // TODO(verwaest): Let SetProperty do the migration, since storing a property 1807 // might deprecate the current map again, if value does not fit. 1808 if (MigrateDeprecated(isolate(), object)) { 1809 // KeyedStoreIC should handle DefineKeyedOwnIC with deprecated maps directly 1810 // instead of reusing this method. 1811 DCHECK(!IsDefineKeyedOwnIC()); 1812 DCHECK(!name->IsPrivateName()); 1813 1814 PropertyKey key(isolate(), name); 1815 LookupIterator it( 1816 isolate(), object, key, 1817 IsDefineNamedOwnIC() ? LookupIterator::OWN : LookupIterator::DEFAULT); 1818 DCHECK_IMPLIES(IsDefineNamedOwnIC(), it.IsFound() && it.HolderIsReceiver()); 1819 // TODO(v8:12548): refactor DefinedNamedOwnIC and SetNamedIC as subclasses 1820 // of StoreIC so their logic doesn't get mixed here. 1821 if (IsDefineNamedOwnIC()) { 1822 MAYBE_RETURN_NULL( 1823 JSReceiver::CreateDataProperty(&it, value, Nothing<ShouldThrow>())); 1824 } else { 1825 MAYBE_RETURN_NULL(Object::SetProperty(&it, value, StoreOrigin::kNamed)); 1826 } 1827 return value; 1828 } 1829 1830 bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic; 1831 // If the object is undefined or null it's illegal to try to set any 1832 // properties on it; throw a TypeError in that case. 1833 if (object->IsNullOrUndefined(isolate())) { 1834 if (use_ic) { 1835 // Ensure the IC state progresses. 1836 TRACE_HANDLER_STATS(isolate(), StoreIC_NonReceiver); 1837 update_lookup_start_object_map(object); 1838 SetCache(name, StoreHandler::StoreSlow(isolate())); 1839 TraceIC("StoreIC", name); 1840 } 1841 return TypeError(MessageTemplate::kNonObjectPropertyStoreWithProperty, name, 1842 object); 1843 } 1844 1845 JSObject::MakePrototypesFast(object, kStartAtPrototype, isolate()); 1846 PropertyKey key(isolate(), name); 1847 LookupIterator it( 1848 isolate(), object, key, 1849 IsAnyDefineOwn() ? LookupIterator::OWN : LookupIterator::DEFAULT); 1850 1851 if (name->IsPrivate()) { 1852 if (name->IsPrivateName()) { 1853 DCHECK(!IsDefineNamedOwnIC()); 1854 if (!JSReceiver::CheckPrivateNameStore(&it, IsDefineKeyedOwnIC())) { 1855 return MaybeHandle<Object>(); 1856 } 1857 } 1858 1859 // IC handling of private fields/symbols stores on JSProxy is not 1860 // supported. 1861 if (object->IsJSProxy()) { 1862 use_ic = false; 1863 } 1864 } 1865 1866 // For IsAnyDefineOwn(), we can't simply do CreateDataProperty below 1867 // because we need to check the attributes before UpdateCaches updates 1868 // the state of the LookupIterator. 1869 LookupIterator::State original_state = it.state(); 1870 // We'll defer the check for JSProxy and objects with named interceptors, 1871 // because the defineProperty traps need to be called first if they are 1872 // present. We can also skip this for private names since they are not 1873 // bound by configurability or extensibility checks, and errors would've 1874 // been thrown if the private field already exists in the object. 1875 if (IsAnyDefineOwn() && !name->IsPrivateName() && !object->IsJSProxy() && 1876 !Handle<JSObject>::cast(object)->HasNamedInterceptor()) { 1877 Maybe<bool> can_define = JSReceiver::CheckIfCanDefine( 1878 isolate(), &it, value, Nothing<ShouldThrow>()); 1879 if (can_define.IsNothing() || !can_define.FromJust()) { 1880 return MaybeHandle<Object>(); 1881 } 1882 } 1883 1884 if (use_ic) { 1885 UpdateCaches(&it, value, store_origin); 1886 } else if (state() == NO_FEEDBACK) { 1887 // Tracing IC Stats for No Feedback State. 1888 IsStoreGlobalIC() ? TraceIC("StoreGlobalIC", name) 1889 : TraceIC("StoreIC", name); 1890 } 1891 1892 // TODO(v8:12548): refactor DefinedNamedOwnIC and SetNamedIC as subclasses 1893 // of StoreIC so their logic doesn't get mixed here. 1894 // ES #sec-definefield 1895 // ES #sec-runtime-semantics-propertydefinitionevaluation 1896 // IsAnyDefineOwn() can be true when this method is reused by KeyedStoreIC. 1897 if (IsAnyDefineOwn()) { 1898 if (name->IsPrivateName()) { 1899 // We should define private fields without triggering traps or checking 1900 // extensibility. 1901 MAYBE_RETURN_NULL( 1902 JSReceiver::AddPrivateField(&it, value, Nothing<ShouldThrow>())); 1903 } else { 1904 MAYBE_RETURN_NULL(DefineOwnDataProperty( 1905 &it, original_state, value, Nothing<ShouldThrow>(), store_origin)); 1906 } 1907 } else { 1908 MAYBE_RETURN_NULL(Object::SetProperty(&it, value, store_origin)); 1909 } 1910 return value; 1911} 1912 1913void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value, 1914 StoreOrigin store_origin) { 1915 MaybeObjectHandle handler; 1916 if (LookupForWrite(lookup, value, store_origin)) { 1917 if (IsStoreGlobalIC()) { 1918 if (lookup->state() == LookupIterator::DATA && 1919 lookup->GetReceiver().is_identical_to(lookup->GetHolder<Object>())) { 1920 DCHECK(lookup->GetReceiver()->IsJSGlobalObject()); 1921 // Now update the cell in the feedback vector. 1922 nexus()->ConfigurePropertyCellMode(lookup->GetPropertyCell()); 1923 TraceIC("StoreGlobalIC", lookup->GetName()); 1924 return; 1925 } 1926 } 1927 handler = ComputeHandler(lookup); 1928 } else { 1929 set_slow_stub_reason("LookupForWrite said 'false'"); 1930 handler = MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 1931 } 1932 // Can't use {lookup->name()} because the LookupIterator might be in 1933 // "elements" mode for keys that are strings representing integers above 1934 // JSArray::kMaxIndex. 1935 SetCache(lookup->GetName(), handler); 1936 TraceIC("StoreIC", lookup->GetName()); 1937} 1938 1939MaybeObjectHandle StoreIC::ComputeHandler(LookupIterator* lookup) { 1940 switch (lookup->state()) { 1941 case LookupIterator::TRANSITION: { 1942 Handle<JSObject> store_target = lookup->GetStoreTarget<JSObject>(); 1943 if (store_target->IsJSGlobalObject()) { 1944 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalTransitionDH); 1945 1946 if (lookup_start_object_map()->IsJSGlobalObject()) { 1947 DCHECK(IsStoreGlobalIC()); 1948#ifdef DEBUG 1949 Handle<JSObject> holder = lookup->GetHolder<JSObject>(); 1950 DCHECK_EQ(*lookup->GetReceiver(), *holder); 1951 DCHECK_EQ(*store_target, *holder); 1952#endif 1953 return StoreHandler::StoreGlobal(lookup->transition_cell()); 1954 } 1955 if (IsDefineKeyedOwnIC()) { 1956 // Private field can't be deleted from this global object and can't 1957 // be overwritten, so install slow handler in order to make store IC 1958 // throw if a private name already exists. 1959 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 1960 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 1961 } 1962 1963 Handle<Smi> smi_handler = StoreHandler::StoreGlobalProxy(isolate()); 1964 Handle<Object> handler = StoreHandler::StoreThroughPrototype( 1965 isolate(), lookup_start_object_map(), store_target, smi_handler, 1966 MaybeObjectHandle::Weak(lookup->transition_cell())); 1967 return MaybeObjectHandle(handler); 1968 } 1969 // Dictionary-to-fast transitions are not expected and not supported. 1970 DCHECK_IMPLIES(!lookup->transition_map()->is_dictionary_map(), 1971 !lookup_start_object_map()->is_dictionary_map()); 1972 1973 DCHECK(lookup->IsCacheableTransition()); 1974 if (IsAnyDefineOwn()) { 1975 return StoreHandler::StoreOwnTransition(isolate(), 1976 lookup->transition_map()); 1977 } 1978 return StoreHandler::StoreTransition(isolate(), lookup->transition_map()); 1979 } 1980 1981 case LookupIterator::INTERCEPTOR: { 1982 Handle<JSObject> holder = lookup->GetHolder<JSObject>(); 1983 InterceptorInfo info = holder->GetNamedInterceptor(); 1984 1985 // If the interceptor is on the receiver... 1986 if (lookup->HolderIsReceiverOrHiddenPrototype() && !info.non_masking()) { 1987 // ...return a store interceptor Smi handler if there is a setter 1988 // interceptor and it's not DefineNamedOwnIC or DefineKeyedOwnIC 1989 // (which should call the definer)... 1990 if (!info.setter().IsUndefined(isolate()) && !IsAnyDefineOwn()) { 1991 return MaybeObjectHandle(StoreHandler::StoreInterceptor(isolate())); 1992 } 1993 // ...otherwise return a slow-case Smi handler, which invokes the 1994 // definer for DefineNamedOwnIC. 1995 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 1996 } 1997 1998 // If the interceptor is a getter/query interceptor on the prototype 1999 // chain, return an invalidatable slow handler so it can turn fast if the 2000 // interceptor is masked by a regular property later. 2001 DCHECK(!info.getter().IsUndefined(isolate()) || 2002 !info.query().IsUndefined(isolate())); 2003 Handle<Object> handler = StoreHandler::StoreThroughPrototype( 2004 isolate(), lookup_start_object_map(), holder, 2005 StoreHandler::StoreSlow(isolate())); 2006 return MaybeObjectHandle(handler); 2007 } 2008 2009 case LookupIterator::ACCESSOR: { 2010 // This is currently guaranteed by checks in StoreIC::Store. 2011 Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver()); 2012 Handle<JSObject> holder = lookup->GetHolder<JSObject>(); 2013 DCHECK(!receiver->IsAccessCheckNeeded() || lookup->name()->IsPrivate()); 2014 2015 if (!holder->HasFastProperties()) { 2016 set_slow_stub_reason("accessor on slow map"); 2017 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2018 MaybeObjectHandle handler = 2019 MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2020 return handler; 2021 } 2022 Handle<Object> accessors = lookup->GetAccessors(); 2023 if (accessors->IsAccessorInfo()) { 2024 Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors); 2025 if (v8::ToCData<Address>(info->setter()) == kNullAddress) { 2026 set_slow_stub_reason("setter == kNullAddress"); 2027 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2028 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2029 } 2030 if (AccessorInfo::cast(*accessors).is_special_data_property() && 2031 !lookup->HolderIsReceiverOrHiddenPrototype()) { 2032 set_slow_stub_reason("special data property in prototype chain"); 2033 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2034 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2035 } 2036 if (!AccessorInfo::IsCompatibleReceiverMap(info, 2037 lookup_start_object_map())) { 2038 set_slow_stub_reason("incompatible receiver type"); 2039 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2040 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2041 } 2042 2043 Handle<Smi> smi_handler = StoreHandler::StoreNativeDataProperty( 2044 isolate(), lookup->GetAccessorIndex()); 2045 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreNativeDataPropertyDH); 2046 if (receiver.is_identical_to(holder)) { 2047 return MaybeObjectHandle(smi_handler); 2048 } 2049 TRACE_HANDLER_STATS(isolate(), 2050 StoreIC_StoreNativeDataPropertyOnPrototypeDH); 2051 return MaybeObjectHandle(StoreHandler::StoreThroughPrototype( 2052 isolate(), lookup_start_object_map(), holder, smi_handler)); 2053 2054 } else if (accessors->IsAccessorPair()) { 2055 Handle<Object> setter(Handle<AccessorPair>::cast(accessors)->setter(), 2056 isolate()); 2057 if (!setter->IsJSFunction() && !setter->IsFunctionTemplateInfo()) { 2058 set_slow_stub_reason("setter not a function"); 2059 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2060 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2061 } 2062 2063 if ((setter->IsFunctionTemplateInfo() && 2064 FunctionTemplateInfo::cast(*setter).BreakAtEntry()) || 2065 (setter->IsJSFunction() && 2066 JSFunction::cast(*setter).shared().BreakAtEntry())) { 2067 // Do not install an IC if the api function has a breakpoint. 2068 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2069 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2070 } 2071 2072 CallOptimization call_optimization(isolate(), setter); 2073 if (call_optimization.is_simple_api_call()) { 2074 CallOptimization::HolderLookup holder_lookup; 2075 Handle<JSObject> api_holder = 2076 call_optimization.LookupHolderOfExpectedType( 2077 isolate(), lookup_start_object_map(), &holder_lookup); 2078 if (call_optimization.IsCompatibleReceiverMap(api_holder, holder, 2079 holder_lookup)) { 2080 Handle<Smi> smi_handler = StoreHandler::StoreApiSetter( 2081 isolate(), 2082 holder_lookup == CallOptimization::kHolderIsReceiver); 2083 2084 Handle<Context> context( 2085 call_optimization.GetAccessorContext(holder->map()), isolate()); 2086 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreApiSetterOnPrototypeDH); 2087 return MaybeObjectHandle(StoreHandler::StoreThroughPrototype( 2088 isolate(), lookup_start_object_map(), holder, smi_handler, 2089 MaybeObjectHandle::Weak(call_optimization.api_call_info()), 2090 MaybeObjectHandle::Weak(context))); 2091 } 2092 set_slow_stub_reason("incompatible receiver"); 2093 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2094 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2095 } else if (setter->IsFunctionTemplateInfo()) { 2096 set_slow_stub_reason("setter non-simple template"); 2097 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2098 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2099 } 2100 2101 Handle<Smi> smi_handler = 2102 StoreHandler::StoreAccessor(isolate(), lookup->GetAccessorIndex()); 2103 2104 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreAccessorDH); 2105 if (receiver.is_identical_to(holder)) { 2106 return MaybeObjectHandle(smi_handler); 2107 } 2108 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreAccessorOnPrototypeDH); 2109 2110 return MaybeObjectHandle(StoreHandler::StoreThroughPrototype( 2111 isolate(), lookup_start_object_map(), holder, smi_handler)); 2112 } 2113 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2114 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2115 } 2116 2117 case LookupIterator::DATA: { 2118 // This is currently guaranteed by checks in StoreIC::Store. 2119 Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver()); 2120 USE(receiver); 2121 Handle<JSObject> holder = lookup->GetHolder<JSObject>(); 2122 DCHECK(!receiver->IsAccessCheckNeeded() || lookup->name()->IsPrivate()); 2123 2124 DCHECK_EQ(PropertyKind::kData, lookup->property_details().kind()); 2125 if (lookup->is_dictionary_holder()) { 2126 if (holder->IsJSGlobalObject()) { 2127 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalDH); 2128 return MaybeObjectHandle( 2129 StoreHandler::StoreGlobal(lookup->GetPropertyCell())); 2130 } 2131 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreNormalDH); 2132 DCHECK(holder.is_identical_to(receiver)); 2133 DCHECK_IMPLIES(!V8_DICT_PROPERTY_CONST_TRACKING_BOOL, 2134 lookup->constness() == PropertyConstness::kMutable); 2135 2136 Handle<Smi> handler = StoreHandler::StoreNormal(isolate()); 2137 return MaybeObjectHandle(handler); 2138 } 2139 2140 // -------------- Elements (for TypedArrays) ------------- 2141 if (lookup->IsElement(*holder)) { 2142 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2143 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2144 } 2145 2146 // -------------- Fields -------------- 2147 if (lookup->property_details().location() == PropertyLocation::kField) { 2148 TRACE_HANDLER_STATS(isolate(), StoreIC_StoreFieldDH); 2149 int descriptor = lookup->GetFieldDescriptorIndex(); 2150 FieldIndex index = lookup->GetFieldIndex(); 2151 if (V8_UNLIKELY(holder->IsJSSharedStruct())) { 2152 return MaybeObjectHandle(StoreHandler::StoreSharedStructField( 2153 isolate(), descriptor, index, lookup->representation())); 2154 } 2155 PropertyConstness constness = lookup->constness(); 2156 if (constness == PropertyConstness::kConst && 2157 IsDefineNamedOwnICKind(nexus()->kind())) { 2158 // DefineNamedOwnICs are used for initializing object literals 2159 // therefore we must store the value unconditionally even to 2160 // VariableMode::kConst fields. 2161 constness = PropertyConstness::kMutable; 2162 } 2163 return MaybeObjectHandle(StoreHandler::StoreField( 2164 isolate(), descriptor, index, constness, lookup->representation())); 2165 } 2166 2167 // -------------- Constant properties -------------- 2168 DCHECK_EQ(PropertyLocation::kDescriptor, 2169 lookup->property_details().location()); 2170 set_slow_stub_reason("constant property"); 2171 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2172 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2173 } 2174 case LookupIterator::JSPROXY: { 2175 Handle<JSReceiver> receiver = 2176 Handle<JSReceiver>::cast(lookup->GetReceiver()); 2177 Handle<JSProxy> holder = lookup->GetHolder<JSProxy>(); 2178 2179 // IsDefineNamedOwnIC() is true when we are defining public fields on a 2180 // Proxy. In that case use the slow stub to invoke the define trap. 2181 if (IsDefineNamedOwnIC()) { 2182 TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub); 2183 return MaybeObjectHandle(StoreHandler::StoreSlow(isolate())); 2184 } 2185 2186 return MaybeObjectHandle(StoreHandler::StoreProxy( 2187 isolate(), lookup_start_object_map(), holder, receiver)); 2188 } 2189 2190 case LookupIterator::INTEGER_INDEXED_EXOTIC: 2191 case LookupIterator::ACCESS_CHECK: 2192 case LookupIterator::NOT_FOUND: 2193 UNREACHABLE(); 2194 } 2195 return MaybeObjectHandle(); 2196} 2197 2198void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map, 2199 KeyedAccessStoreMode store_mode, 2200 Handle<Map> new_receiver_map) { 2201 std::vector<MapAndHandler> target_maps_and_handlers; 2202 nexus()->ExtractMapsAndHandlers( 2203 &target_maps_and_handlers, 2204 [this](Handle<Map> map) { return Map::TryUpdate(isolate(), map); }); 2205 if (target_maps_and_handlers.empty()) { 2206 Handle<Map> monomorphic_map = receiver_map; 2207 // If we transitioned to a map that is a more general map than incoming 2208 // then use the new map. 2209 if (IsTransitionOfMonomorphicTarget(*receiver_map, *new_receiver_map)) { 2210 monomorphic_map = new_receiver_map; 2211 } 2212 Handle<Object> handler = StoreElementHandler(monomorphic_map, store_mode); 2213 return ConfigureVectorState(Handle<Name>(), monomorphic_map, handler); 2214 } 2215 2216 for (const MapAndHandler& map_and_handler : target_maps_and_handlers) { 2217 Handle<Map> map = map_and_handler.first; 2218 if (!map.is_null() && map->instance_type() == JS_PRIMITIVE_WRAPPER_TYPE) { 2219 DCHECK(!IsStoreInArrayLiteralIC()); 2220 set_slow_stub_reason("JSPrimitiveWrapper"); 2221 return; 2222 } 2223 } 2224 2225 // There are several special cases where an IC that is MONOMORPHIC can still 2226 // transition to a different IC that handles a superset of the original IC. 2227 // Handle those here if the receiver map hasn't changed or it has transitioned 2228 // to a more general kind. 2229 KeyedAccessStoreMode old_store_mode = GetKeyedAccessStoreMode(); 2230 Handle<Map> previous_receiver_map = target_maps_and_handlers.at(0).first; 2231 if (state() == MONOMORPHIC) { 2232 Handle<Map> transitioned_receiver_map = new_receiver_map; 2233 if (IsTransitionOfMonomorphicTarget(*previous_receiver_map, 2234 *transitioned_receiver_map)) { 2235 // If the "old" and "new" maps are in the same elements map family, or 2236 // if they at least come from the same origin for a transitioning store, 2237 // stay MONOMORPHIC and use the map for the most generic ElementsKind. 2238 Handle<Object> handler = 2239 StoreElementHandler(transitioned_receiver_map, store_mode); 2240 ConfigureVectorState(Handle<Name>(), transitioned_receiver_map, handler); 2241 return; 2242 } 2243 // If there is no transition and if we have seen the same map earlier and 2244 // there is only a change in the store_mode we can still stay monomorphic. 2245 if (receiver_map.is_identical_to(previous_receiver_map) && 2246 new_receiver_map.is_identical_to(receiver_map) && 2247 old_store_mode == STANDARD_STORE && store_mode != STANDARD_STORE) { 2248 if (receiver_map->IsJSArrayMap() && 2249 JSArray::MayHaveReadOnlyLength(*receiver_map)) { 2250 set_slow_stub_reason( 2251 "can't generalize store mode (potentially read-only length)"); 2252 return; 2253 } 2254 // A "normal" IC that handles stores can switch to a version that can 2255 // grow at the end of the array, handle OOB accesses or copy COW arrays 2256 // and still stay MONOMORPHIC. 2257 Handle<Object> handler = StoreElementHandler(receiver_map, store_mode); 2258 return ConfigureVectorState(Handle<Name>(), receiver_map, handler); 2259 } 2260 } 2261 2262 DCHECK(state() != GENERIC); 2263 2264 bool map_added = 2265 AddOneReceiverMapIfMissing(&target_maps_and_handlers, receiver_map); 2266 2267 if (IsTransitionOfMonomorphicTarget(*receiver_map, *new_receiver_map)) { 2268 map_added |= 2269 AddOneReceiverMapIfMissing(&target_maps_and_handlers, new_receiver_map); 2270 } 2271 2272 if (!map_added) { 2273 // If the miss wasn't due to an unseen map, a polymorphic stub 2274 // won't help, use the megamorphic stub which can handle everything. 2275 set_slow_stub_reason("same map added twice"); 2276 return; 2277 } 2278 2279 // If the maximum number of receiver maps has been exceeded, use the 2280 // megamorphic version of the IC. 2281 if (static_cast<int>(target_maps_and_handlers.size()) > 2282 FLAG_max_valid_polymorphic_map_count) { 2283 return; 2284 } 2285 2286 // Make sure all polymorphic handlers have the same store mode, otherwise the 2287 // megamorphic stub must be used. 2288 if (old_store_mode != STANDARD_STORE) { 2289 if (store_mode == STANDARD_STORE) { 2290 store_mode = old_store_mode; 2291 } else if (store_mode != old_store_mode) { 2292 set_slow_stub_reason("store mode mismatch"); 2293 return; 2294 } 2295 } 2296 2297 // If the store mode isn't the standard mode, make sure that all polymorphic 2298 // receivers are either external arrays, or all "normal" arrays with writable 2299 // length. Otherwise, use the megamorphic stub. 2300 if (store_mode != STANDARD_STORE) { 2301 size_t external_arrays = 0; 2302 for (MapAndHandler map_and_handler : target_maps_and_handlers) { 2303 Handle<Map> map = map_and_handler.first; 2304 if (map->IsJSArrayMap() && JSArray::MayHaveReadOnlyLength(*map)) { 2305 set_slow_stub_reason( 2306 "unsupported combination of arrays (potentially read-only length)"); 2307 return; 2308 2309 } else if (map->has_typed_array_or_rab_gsab_typed_array_elements()) { 2310 DCHECK(!IsStoreInArrayLiteralIC()); 2311 external_arrays++; 2312 } 2313 } 2314 if (external_arrays != 0 && 2315 external_arrays != target_maps_and_handlers.size()) { 2316 DCHECK(!IsStoreInArrayLiteralIC()); 2317 set_slow_stub_reason( 2318 "unsupported combination of external and normal arrays"); 2319 return; 2320 } 2321 } 2322 2323 StoreElementPolymorphicHandlers(&target_maps_and_handlers, store_mode); 2324 if (target_maps_and_handlers.size() == 0) { 2325 Handle<Object> handler = StoreElementHandler(receiver_map, store_mode); 2326 ConfigureVectorState(Handle<Name>(), receiver_map, handler); 2327 } else if (target_maps_and_handlers.size() == 1) { 2328 ConfigureVectorState(Handle<Name>(), target_maps_and_handlers[0].first, 2329 target_maps_and_handlers[0].second); 2330 } else { 2331 ConfigureVectorState(Handle<Name>(), target_maps_and_handlers); 2332 } 2333} 2334 2335Handle<Object> KeyedStoreIC::StoreElementHandler( 2336 Handle<Map> receiver_map, KeyedAccessStoreMode store_mode, 2337 MaybeHandle<Object> prev_validity_cell) { 2338 // The only case when could keep using non-slow element store handler for 2339 // a fast array with potentially read-only elements is when it's an 2340 // initializing store to array literal. 2341 DCHECK_IMPLIES( 2342 !receiver_map->has_dictionary_elements() && 2343 receiver_map->MayHaveReadOnlyElementsInPrototypeChain(isolate()), 2344 IsStoreInArrayLiteralIC()); 2345 2346 if (receiver_map->IsJSProxyMap()) { 2347 return StoreHandler::StoreProxy(isolate()); 2348 } 2349 2350 // TODO(ishell): move to StoreHandler::StoreElement(). 2351 Handle<Object> code; 2352 if (receiver_map->has_sloppy_arguments_elements()) { 2353 // TODO(jgruber): Update counter name. 2354 TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_KeyedStoreSloppyArgumentsStub); 2355 code = CodeHandler(StoreHandler::StoreSloppyArgumentsBuiltin(store_mode)); 2356 } else if (receiver_map->has_fast_elements() || 2357 receiver_map->has_sealed_elements() || 2358 receiver_map->has_nonextensible_elements() || 2359 receiver_map->has_typed_array_or_rab_gsab_typed_array_elements()) { 2360 TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreFastElementStub); 2361 code = CodeHandler(StoreHandler::StoreFastElementBuiltin(store_mode)); 2362 if (receiver_map->has_typed_array_or_rab_gsab_typed_array_elements()) { 2363 return code; 2364 } 2365 } else if (IsStoreInArrayLiteralIC()) { 2366 // TODO(jgruber): Update counter name. 2367 TRACE_HANDLER_STATS(isolate(), StoreInArrayLiteralIC_SlowStub); 2368 return StoreHandler::StoreSlow(isolate(), store_mode); 2369 } else { 2370 // TODO(jgruber): Update counter name. 2371 TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_StoreElementStub); 2372 DCHECK(DICTIONARY_ELEMENTS == receiver_map->elements_kind() || 2373 receiver_map->has_frozen_elements()); 2374 code = StoreHandler::StoreSlow(isolate(), store_mode); 2375 } 2376 2377 if (IsAnyDefineOwn() || IsStoreInArrayLiteralIC()) return code; 2378 Handle<Object> validity_cell; 2379 if (!prev_validity_cell.ToHandle(&validity_cell)) { 2380 validity_cell = 2381 Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate()); 2382 } 2383 if (validity_cell->IsSmi()) { 2384 // There's no prototype validity cell to check, so we can just use the stub. 2385 return code; 2386 } 2387 Handle<StoreHandler> handler = isolate()->factory()->NewStoreHandler(0); 2388 handler->set_validity_cell(*validity_cell); 2389 handler->set_smi_handler(*code); 2390 return handler; 2391} 2392 2393void KeyedStoreIC::StoreElementPolymorphicHandlers( 2394 std::vector<MapAndHandler>* receiver_maps_and_handlers, 2395 KeyedAccessStoreMode store_mode) { 2396 std::vector<Handle<Map>> receiver_maps; 2397 for (size_t i = 0; i < receiver_maps_and_handlers->size(); i++) { 2398 receiver_maps.push_back(receiver_maps_and_handlers->at(i).first); 2399 } 2400 2401 for (size_t i = 0; i < receiver_maps_and_handlers->size(); i++) { 2402 Handle<Map> receiver_map = receiver_maps_and_handlers->at(i).first; 2403 DCHECK(!receiver_map->is_deprecated()); 2404 MaybeObjectHandle old_handler = receiver_maps_and_handlers->at(i).second; 2405 Handle<Object> handler; 2406 Handle<Map> transition; 2407 2408 if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE || 2409 receiver_map->MayHaveReadOnlyElementsInPrototypeChain(isolate())) { 2410 // TODO(mvstanton): Consider embedding store_mode in the state of the slow 2411 // keyed store ic for uniformity. 2412 TRACE_HANDLER_STATS(isolate(), KeyedStoreIC_SlowStub); 2413 handler = StoreHandler::StoreSlow(isolate()); 2414 2415 } else { 2416 { 2417 Map tmap = receiver_map->FindElementsKindTransitionedMap( 2418 isolate(), receiver_maps, ConcurrencyMode::kSynchronous); 2419 if (!tmap.is_null()) { 2420 if (receiver_map->is_stable()) { 2421 receiver_map->NotifyLeafMapLayoutChange(isolate()); 2422 } 2423 transition = handle(tmap, isolate()); 2424 } 2425 } 2426 2427 MaybeHandle<Object> validity_cell; 2428 HeapObject old_handler_obj; 2429 if (!old_handler.is_null() && 2430 old_handler->GetHeapObject(&old_handler_obj) && 2431 old_handler_obj.IsDataHandler()) { 2432 validity_cell = MaybeHandle<Object>( 2433 DataHandler::cast(old_handler_obj).validity_cell(), isolate()); 2434 } 2435 // TODO(mythria): Do not recompute the handler if we know there is no 2436 // change in the handler. 2437 // TODO(mvstanton): The code below is doing pessimistic elements 2438 // transitions. I would like to stop doing that and rely on Allocation 2439 // Site Tracking to do a better job of ensuring the data types are what 2440 // they need to be. Not all the elements are in place yet, pessimistic 2441 // elements transitions are still important for performance. 2442 if (!transition.is_null()) { 2443 TRACE_HANDLER_STATS(isolate(), 2444 KeyedStoreIC_ElementsTransitionAndStoreStub); 2445 handler = StoreHandler::StoreElementTransition( 2446 isolate(), receiver_map, transition, store_mode, validity_cell); 2447 } else { 2448 handler = StoreElementHandler(receiver_map, store_mode, validity_cell); 2449 } 2450 } 2451 DCHECK(!handler.is_null()); 2452 receiver_maps_and_handlers->at(i) = 2453 MapAndHandler(receiver_map, MaybeObjectHandle(handler)); 2454 } 2455} 2456 2457namespace { 2458 2459bool MayHaveTypedArrayInPrototypeChain(Handle<JSObject> object) { 2460 for (PrototypeIterator iter(object->GetIsolate(), *object); !iter.IsAtEnd(); 2461 iter.Advance()) { 2462 // Be conservative, don't walk into proxies. 2463 if (iter.GetCurrent().IsJSProxy()) return true; 2464 if (iter.GetCurrent().IsJSTypedArray()) return true; 2465 } 2466 return false; 2467} 2468 2469KeyedAccessStoreMode GetStoreMode(Handle<JSObject> receiver, size_t index) { 2470 bool oob_access = IsOutOfBoundsAccess(receiver, index); 2471 // Don't consider this a growing store if the store would send the receiver to 2472 // dictionary mode. 2473 bool allow_growth = 2474 receiver->IsJSArray() && oob_access && index <= JSArray::kMaxArrayIndex && 2475 !receiver->WouldConvertToSlowElements(static_cast<uint32_t>(index)); 2476 if (allow_growth) { 2477 return STORE_AND_GROW_HANDLE_COW; 2478 } 2479 if (receiver->map().has_typed_array_or_rab_gsab_typed_array_elements() && 2480 oob_access) { 2481 return STORE_IGNORE_OUT_OF_BOUNDS; 2482 } 2483 return receiver->elements().IsCowArray() ? STORE_HANDLE_COW : STANDARD_STORE; 2484} 2485 2486} // namespace 2487 2488MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object, 2489 Handle<Object> key, 2490 Handle<Object> value) { 2491 // TODO(verwaest): Let SetProperty do the migration, since storing a property 2492 // might deprecate the current map again, if value does not fit. 2493 if (MigrateDeprecated(isolate(), object)) { 2494 Handle<Object> result; 2495 // TODO(v8:12548): refactor DefineKeyedOwnIC as a subclass of StoreIC 2496 // so the logic doesn't get mixed here. 2497 ASSIGN_RETURN_ON_EXCEPTION( 2498 isolate(), result, 2499 IsDefineKeyedOwnIC() 2500 ? Runtime::DefineObjectOwnProperty(isolate(), object, key, value, 2501 StoreOrigin::kMaybeKeyed) 2502 : Runtime::SetObjectProperty(isolate(), object, key, value, 2503 StoreOrigin::kMaybeKeyed), 2504 Object); 2505 return result; 2506 } 2507 2508 Handle<Object> store_handle; 2509 2510 intptr_t maybe_index; 2511 Handle<Name> maybe_name; 2512 KeyType key_type = TryConvertKey(key, isolate(), &maybe_index, &maybe_name); 2513 2514 if (key_type == kName) { 2515 ASSIGN_RETURN_ON_EXCEPTION( 2516 isolate(), store_handle, 2517 StoreIC::Store(object, maybe_name, value, StoreOrigin::kMaybeKeyed), 2518 Object); 2519 if (vector_needs_update()) { 2520 if (ConfigureVectorState(MEGAMORPHIC, key)) { 2521 set_slow_stub_reason("unhandled internalized string key"); 2522 TraceIC("StoreIC", key); 2523 } 2524 } 2525 return store_handle; 2526 } 2527 2528 JSObject::MakePrototypesFast(object, kStartAtPrototype, isolate()); 2529 2530 // TODO(jkummerow): Refactor the condition logic here and below. 2531 bool use_ic = (state() != NO_FEEDBACK) && FLAG_use_ic && 2532 !object->IsStringWrapper() && !object->IsAccessCheckNeeded() && 2533 !object->IsJSGlobalProxy(); 2534 if (use_ic && !object->IsSmi()) { 2535 // Don't use ICs for maps of the objects in Array's prototype chain. We 2536 // expect to be able to trap element sets to objects with those maps in 2537 // the runtime to enable optimization of element hole access. 2538 Handle<HeapObject> heap_object = Handle<HeapObject>::cast(object); 2539 if (heap_object->map().IsMapInArrayPrototypeChain(isolate())) { 2540 set_slow_stub_reason("map in array prototype"); 2541 use_ic = false; 2542 } 2543 } 2544 2545 Handle<Map> old_receiver_map; 2546 bool is_arguments = false; 2547 bool key_is_valid_index = (key_type == kIntPtr); 2548 KeyedAccessStoreMode store_mode = STANDARD_STORE; 2549 if (use_ic && object->IsJSReceiver() && key_is_valid_index) { 2550 Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object); 2551 old_receiver_map = handle(receiver->map(), isolate()); 2552 is_arguments = receiver->IsJSArgumentsObject(); 2553 bool is_proxy = receiver->IsJSProxy(); 2554 size_t index; 2555 key_is_valid_index = IntPtrKeyToSize(maybe_index, receiver, &index); 2556 if (!is_arguments && !is_proxy) { 2557 if (key_is_valid_index) { 2558 Handle<JSObject> receiver_object = Handle<JSObject>::cast(object); 2559 store_mode = GetStoreMode(receiver_object, index); 2560 } 2561 } 2562 } 2563 2564 DCHECK(store_handle.is_null()); 2565 ASSIGN_RETURN_ON_EXCEPTION( 2566 isolate(), store_handle, 2567 // TODO(v8:12548): refactor DefineKeyedOwnIC as a subclass of StoreIC 2568 // so the logic doesn't get mixed here. 2569 IsDefineKeyedOwnIC() 2570 ? Runtime::DefineObjectOwnProperty(isolate(), object, key, value, 2571 StoreOrigin::kMaybeKeyed) 2572 : Runtime::SetObjectProperty(isolate(), object, key, value, 2573 StoreOrigin::kMaybeKeyed), 2574 Object); 2575 if (use_ic) { 2576 if (!old_receiver_map.is_null()) { 2577 if (is_arguments) { 2578 set_slow_stub_reason("arguments receiver"); 2579 } else if (object->IsJSArray() && IsGrowStoreMode(store_mode) && 2580 JSArray::HasReadOnlyLength(Handle<JSArray>::cast(object))) { 2581 set_slow_stub_reason("array has read only length"); 2582 } else if (object->IsJSObject() && MayHaveTypedArrayInPrototypeChain( 2583 Handle<JSObject>::cast(object))) { 2584 // Make sure we don't handle this in IC if there's any JSTypedArray in 2585 // the {receiver}'s prototype chain, since that prototype is going to 2586 // swallow all stores that are out-of-bounds for said prototype, and we 2587 // just let the runtime deal with the complexity of this. 2588 set_slow_stub_reason("typed array in the prototype chain"); 2589 } else if (key_is_valid_index) { 2590 if (old_receiver_map->is_abandoned_prototype_map()) { 2591 set_slow_stub_reason("receiver with prototype map"); 2592 } else if (old_receiver_map->has_dictionary_elements() || 2593 !old_receiver_map->MayHaveReadOnlyElementsInPrototypeChain( 2594 isolate())) { 2595 // We should go generic if receiver isn't a dictionary, but our 2596 // prototype chain does have dictionary elements. This ensures that 2597 // other non-dictionary receivers in the polymorphic case benefit 2598 // from fast path keyed stores. 2599 Handle<HeapObject> receiver = Handle<HeapObject>::cast(object); 2600 UpdateStoreElement(old_receiver_map, store_mode, 2601 handle(receiver->map(), isolate())); 2602 } else { 2603 set_slow_stub_reason("prototype with potentially read-only elements"); 2604 } 2605 } else { 2606 set_slow_stub_reason("non-smi-like key"); 2607 } 2608 } else { 2609 set_slow_stub_reason("non-JSObject receiver"); 2610 } 2611 } 2612 2613 if (vector_needs_update()) { 2614 ConfigureVectorState(MEGAMORPHIC, key); 2615 } 2616 TraceIC("StoreIC", key); 2617 2618 return store_handle; 2619} 2620 2621namespace { 2622Maybe<bool> StoreOwnElement(Isolate* isolate, Handle<JSArray> array, 2623 Handle<Object> index, Handle<Object> value) { 2624 DCHECK(index->IsNumber()); 2625 PropertyKey key(isolate, index); 2626 LookupIterator it(isolate, array, key, LookupIterator::OWN); 2627 2628 MAYBE_RETURN(JSObject::DefineOwnPropertyIgnoreAttributes( 2629 &it, value, NONE, Just(ShouldThrow::kThrowOnError)), 2630 Nothing<bool>()); 2631 return Just(true); 2632} 2633} // namespace 2634 2635MaybeHandle<Object> StoreInArrayLiteralIC::Store(Handle<JSArray> array, 2636 Handle<Object> index, 2637 Handle<Object> value) { 2638 DCHECK(!array->map().IsMapInArrayPrototypeChain(isolate())); 2639 DCHECK(index->IsNumber()); 2640 2641 if (!FLAG_use_ic || state() == NO_FEEDBACK || 2642 MigrateDeprecated(isolate(), array)) { 2643 MAYBE_RETURN_NULL(StoreOwnElement(isolate(), array, index, value)); 2644 TraceIC("StoreInArrayLiteralIC", index); 2645 return value; 2646 } 2647 2648 // TODO(neis): Convert HeapNumber to Smi if possible? 2649 2650 KeyedAccessStoreMode store_mode = STANDARD_STORE; 2651 if (index->IsSmi()) { 2652 DCHECK_GE(Smi::ToInt(*index), 0); 2653 uint32_t index32 = static_cast<uint32_t>(Smi::ToInt(*index)); 2654 store_mode = GetStoreMode(array, index32); 2655 } 2656 2657 Handle<Map> old_array_map(array->map(), isolate()); 2658 MAYBE_RETURN_NULL(StoreOwnElement(isolate(), array, index, value)); 2659 2660 if (index->IsSmi()) { 2661 DCHECK(!old_array_map->is_abandoned_prototype_map()); 2662 UpdateStoreElement(old_array_map, store_mode, 2663 handle(array->map(), isolate())); 2664 } else { 2665 set_slow_stub_reason("index out of Smi range"); 2666 } 2667 2668 if (vector_needs_update()) { 2669 ConfigureVectorState(MEGAMORPHIC, index); 2670 } 2671 TraceIC("StoreInArrayLiteralIC", index); 2672 return value; 2673} 2674 2675// ---------------------------------------------------------------------------- 2676// Static IC stub generators. 2677// 2678// 2679RUNTIME_FUNCTION(Runtime_LoadIC_Miss) { 2680 HandleScope scope(isolate); 2681 DCHECK_EQ(4, args.length()); 2682 // Runtime functions don't follow the IC's calling convention. 2683 Handle<Object> receiver = args.at(0); 2684 Handle<Name> key = args.at<Name>(1); 2685 Handle<TaggedIndex> slot = args.at<TaggedIndex>(2); 2686 Handle<FeedbackVector> vector = args.at<FeedbackVector>(3); 2687 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2688 2689 // A monomorphic or polymorphic KeyedLoadIC with a string key can call the 2690 // LoadIC miss handler if the handler misses. Since the vector Nexus is 2691 // set up outside the IC, handle that here. 2692 FeedbackSlotKind kind = vector->GetKind(vector_slot); 2693 if (IsLoadICKind(kind)) { 2694 LoadIC ic(isolate, vector, vector_slot, kind); 2695 ic.UpdateState(receiver, key); 2696 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); 2697 2698 } else if (IsLoadGlobalICKind(kind)) { 2699 DCHECK_EQ(isolate->native_context()->global_proxy(), *receiver); 2700 receiver = isolate->global_object(); 2701 LoadGlobalIC ic(isolate, vector, vector_slot, kind); 2702 ic.UpdateState(receiver, key); 2703 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(key)); 2704 2705 } else { 2706 DCHECK(IsKeyedLoadICKind(kind)); 2707 KeyedLoadIC ic(isolate, vector, vector_slot, kind); 2708 ic.UpdateState(receiver, key); 2709 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); 2710 } 2711} 2712 2713RUNTIME_FUNCTION(Runtime_LoadNoFeedbackIC_Miss) { 2714 HandleScope scope(isolate); 2715 DCHECK_EQ(3, args.length()); 2716 // Runtime functions don't follow the IC's calling convention. 2717 Handle<Object> receiver = args.at(0); 2718 Handle<Name> key = args.at<Name>(1); 2719 int slot_kind = args.smi_value_at(2); 2720 FeedbackSlotKind kind = static_cast<FeedbackSlotKind>(slot_kind); 2721 2722 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 2723 FeedbackSlot vector_slot = FeedbackSlot::Invalid(); 2724 // This function is only called after looking up in the ScriptContextTable so 2725 // it is safe to call LoadIC::Load for global loads as well. 2726 LoadIC ic(isolate, vector, vector_slot, kind); 2727 ic.UpdateState(receiver, key); 2728 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); 2729} 2730 2731RUNTIME_FUNCTION(Runtime_LoadWithReceiverNoFeedbackIC_Miss) { 2732 HandleScope scope(isolate); 2733 DCHECK_EQ(3, args.length()); 2734 // Runtime functions don't follow the IC's calling convention. 2735 Handle<Object> receiver = args.at(0); 2736 Handle<Object> object = args.at(1); 2737 Handle<Name> key = args.at<Name>(2); 2738 2739 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 2740 FeedbackSlot vector_slot = FeedbackSlot::Invalid(); 2741 LoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kLoadProperty); 2742 ic.UpdateState(object, key); 2743 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(object, key, true, receiver)); 2744} 2745 2746RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss) { 2747 HandleScope scope(isolate); 2748 DCHECK_EQ(4, args.length()); 2749 // Runtime functions don't follow the IC's calling convention. 2750 Handle<JSGlobalObject> global = isolate->global_object(); 2751 Handle<String> name = args.at<String>(0); 2752 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 2753 Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); 2754 int typeof_value = args.smi_value_at(3); 2755 TypeofMode typeof_mode = static_cast<TypeofMode>(typeof_value); 2756 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2757 2758 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 2759 if (!maybe_vector->IsUndefined()) { 2760 DCHECK(maybe_vector->IsFeedbackVector()); 2761 vector = Handle<FeedbackVector>::cast(maybe_vector); 2762 } 2763 2764 FeedbackSlotKind kind = (typeof_mode == TypeofMode::kInside) 2765 ? FeedbackSlotKind::kLoadGlobalInsideTypeof 2766 : FeedbackSlotKind::kLoadGlobalNotInsideTypeof; 2767 LoadGlobalIC ic(isolate, vector, vector_slot, kind); 2768 ic.UpdateState(global, name); 2769 2770 Handle<Object> result; 2771 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, ic.Load(name)); 2772 return *result; 2773} 2774 2775RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow) { 2776 HandleScope scope(isolate); 2777 DCHECK_EQ(3, args.length()); 2778 Handle<String> name = args.at<String>(0); 2779 2780 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 2781 Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); 2782 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2783 FeedbackSlotKind kind = vector->GetKind(vector_slot); 2784 2785 LoadGlobalIC ic(isolate, vector, vector_slot, kind); 2786 Handle<Object> result; 2787 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, ic.Load(name, false)); 2788 return *result; 2789} 2790 2791RUNTIME_FUNCTION(Runtime_LoadWithReceiverIC_Miss) { 2792 HandleScope scope(isolate); 2793 DCHECK_EQ(5, args.length()); 2794 // Runtime functions don't follow the IC's calling convention. 2795 Handle<Object> receiver = args.at(0); 2796 Handle<Object> object = args.at(1); 2797 Handle<Name> key = args.at<Name>(2); 2798 Handle<TaggedIndex> slot = args.at<TaggedIndex>(3); 2799 Handle<FeedbackVector> vector = args.at<FeedbackVector>(4); 2800 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2801 2802 DCHECK(IsLoadICKind(vector->GetKind(vector_slot))); 2803 LoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kLoadProperty); 2804 ic.UpdateState(object, key); 2805 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(object, key, true, receiver)); 2806} 2807 2808RUNTIME_FUNCTION(Runtime_KeyedLoadIC_Miss) { 2809 HandleScope scope(isolate); 2810 DCHECK_EQ(4, args.length()); 2811 // Runtime functions don't follow the IC's calling convention. 2812 Handle<Object> receiver = args.at(0); 2813 Handle<Object> key = args.at(1); 2814 Handle<TaggedIndex> slot = args.at<TaggedIndex>(2); 2815 Handle<HeapObject> maybe_vector = args.at<HeapObject>(3); 2816 2817 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 2818 if (!maybe_vector->IsUndefined()) { 2819 DCHECK(maybe_vector->IsFeedbackVector()); 2820 vector = Handle<FeedbackVector>::cast(maybe_vector); 2821 } 2822 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2823 KeyedLoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kLoadKeyed); 2824 ic.UpdateState(receiver, key); 2825 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); 2826} 2827 2828RUNTIME_FUNCTION(Runtime_StoreIC_Miss) { 2829 HandleScope scope(isolate); 2830 DCHECK_EQ(5, args.length()); 2831 // Runtime functions don't follow the IC's calling convention. 2832 Handle<Object> value = args.at(0); 2833 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 2834 Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); 2835 Handle<Object> receiver = args.at(3); 2836 Handle<Name> key = args.at<Name>(4); 2837 2838 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2839 2840 // When there is no feedback vector it is OK to use the SetNamedStrict as 2841 // the feedback slot kind. We only reuse this for DefineNamedOwnIC when 2842 // installing the handler for storing const properties. This will happen only 2843 // when feedback vector is available. 2844 FeedbackSlotKind kind = FeedbackSlotKind::kSetNamedStrict; 2845 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 2846 if (!maybe_vector->IsUndefined()) { 2847 DCHECK(maybe_vector->IsFeedbackVector()); 2848 vector = Handle<FeedbackVector>::cast(maybe_vector); 2849 kind = vector->GetKind(vector_slot); 2850 } 2851 2852 DCHECK(IsSetNamedICKind(kind) || IsDefineNamedOwnICKind(kind)); 2853 StoreIC ic(isolate, vector, vector_slot, kind); 2854 ic.UpdateState(receiver, key); 2855 RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); 2856} 2857 2858RUNTIME_FUNCTION(Runtime_DefineNamedOwnIC_Miss) { 2859 HandleScope scope(isolate); 2860 DCHECK_EQ(5, args.length()); 2861 // Runtime functions don't follow the IC's calling convention. 2862 Handle<Object> value = args.at(0); 2863 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 2864 Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); 2865 Handle<Object> receiver = args.at(3); 2866 Handle<Name> key = args.at<Name>(4); 2867 2868 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2869 2870 // When there is no feedback vector it is OK to use the DefineNamedOwn 2871 // feedback kind. There _should_ be a vector, though. 2872 FeedbackSlotKind kind = FeedbackSlotKind::kDefineNamedOwn; 2873 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 2874 if (!maybe_vector->IsUndefined()) { 2875 DCHECK(maybe_vector->IsFeedbackVector()); 2876 vector = Handle<FeedbackVector>::cast(maybe_vector); 2877 kind = vector->GetKind(vector_slot); 2878 } 2879 2880 DCHECK(IsDefineNamedOwnICKind(kind)); 2881 2882 // TODO(v8:12548): refactor DefineNamedOwnIC as a subclass of StoreIC, which 2883 // can be called here. 2884 StoreIC ic(isolate, vector, vector_slot, kind); 2885 ic.UpdateState(receiver, key); 2886 RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); 2887} 2888 2889RUNTIME_FUNCTION(Runtime_DefineNamedOwnIC_Slow) { 2890 HandleScope scope(isolate); 2891 DCHECK_EQ(3, args.length()); 2892 2893 Handle<Object> value = args.at(0); 2894 Handle<Object> object = args.at(1); 2895 Handle<Object> key = args.at(2); 2896 2897 // Unlike DefineKeyedOwnIC, DefineNamedOwnIC doesn't handle private 2898 // fields and is used for defining data properties in object literals 2899 // and defining named public class fields. 2900 DCHECK(!key->IsSymbol() || !Symbol::cast(*key).is_private_name()); 2901 2902 PropertyKey lookup_key(isolate, key); 2903 LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN); 2904 2905 MAYBE_RETURN( 2906 JSReceiver::CreateDataProperty(&it, value, Nothing<ShouldThrow>()), 2907 ReadOnlyRoots(isolate).exception()); 2908 return *value; 2909} 2910 2911RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Miss) { 2912 HandleScope scope(isolate); 2913 DCHECK_EQ(4, args.length()); 2914 // Runtime functions don't follow the IC's calling convention. 2915 Handle<Object> value = args.at(0); 2916 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 2917 Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); 2918 Handle<Name> key = args.at<Name>(3); 2919 2920 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2921 FeedbackSlotKind kind = vector->GetKind(vector_slot); 2922 StoreGlobalIC ic(isolate, vector, vector_slot, kind); 2923 Handle<JSGlobalObject> global = isolate->global_object(); 2924 ic.UpdateState(global, key); 2925 RETURN_RESULT_OR_FAILURE(isolate, ic.Store(key, value)); 2926} 2927 2928RUNTIME_FUNCTION(Runtime_StoreGlobalICNoFeedback_Miss) { 2929 HandleScope scope(isolate); 2930 DCHECK_EQ(2, args.length()); 2931 // Runtime functions don't follow the IC's calling convention. 2932 Handle<Object> value = args.at(0); 2933 Handle<Name> key = args.at<Name>(1); 2934 2935 // TODO(mythria): Replace StoreGlobalStrict/Sloppy with SetNamedProperty. 2936 StoreGlobalIC ic(isolate, Handle<FeedbackVector>(), FeedbackSlot(), 2937 FeedbackSlotKind::kStoreGlobalStrict); 2938 RETURN_RESULT_OR_FAILURE(isolate, ic.Store(key, value)); 2939} 2940 2941// TODO(mythria): Remove Feedback vector and slot. Since they are not used apart 2942// from the DCHECK. 2943RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Slow) { 2944 HandleScope scope(isolate); 2945 DCHECK_EQ(5, args.length()); 2946 // Runtime functions don't follow the IC's calling convention. 2947 Handle<Object> value = args.at(0); 2948 Handle<String> name = args.at<String>(4); 2949 2950#ifdef DEBUG 2951 { 2952 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 2953 Handle<FeedbackVector> vector = args.at<FeedbackVector>(2); 2954 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 2955 FeedbackSlotKind slot_kind = vector->GetKind(vector_slot); 2956 DCHECK(IsStoreGlobalICKind(slot_kind)); 2957 Handle<Object> receiver = args.at(3); 2958 DCHECK(receiver->IsJSGlobalProxy()); 2959 } 2960#endif 2961 2962 Handle<JSGlobalObject> global = isolate->global_object(); 2963 Handle<Context> native_context = isolate->native_context(); 2964 Handle<ScriptContextTable> script_contexts( 2965 native_context->script_context_table(), isolate); 2966 2967 VariableLookupResult lookup_result; 2968 if (script_contexts->Lookup(name, &lookup_result)) { 2969 Handle<Context> script_context = ScriptContextTable::GetContext( 2970 isolate, script_contexts, lookup_result.context_index); 2971 if (lookup_result.mode == VariableMode::kConst) { 2972 THROW_NEW_ERROR_RETURN_FAILURE( 2973 isolate, NewTypeError(MessageTemplate::kConstAssign, global, name)); 2974 } 2975 2976 Handle<Object> previous_value(script_context->get(lookup_result.slot_index), 2977 isolate); 2978 2979 if (previous_value->IsTheHole(isolate)) { 2980 THROW_NEW_ERROR_RETURN_FAILURE( 2981 isolate, NewReferenceError( 2982 MessageTemplate::kAccessedUninitializedVariable, name)); 2983 } 2984 2985 script_context->set(lookup_result.slot_index, *value); 2986 return *value; 2987 } 2988 2989 RETURN_RESULT_OR_FAILURE( 2990 isolate, Runtime::SetObjectProperty(isolate, global, name, value, 2991 StoreOrigin::kMaybeKeyed)); 2992} 2993 2994RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) { 2995 HandleScope scope(isolate); 2996 DCHECK_EQ(5, args.length()); 2997 // Runtime functions don't follow the IC's calling convention. 2998 Handle<Object> value = args.at(0); 2999 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 3000 Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); 3001 Handle<Object> receiver = args.at(3); 3002 Handle<Object> key = args.at(4); 3003 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 3004 3005 // When the feedback vector is not valid the slot can only be of type 3006 // StoreKeyed. Storing in array literals falls back to 3007 // StoreInArrayLiterIC_Miss. This function is also used from store handlers 3008 // installed in feedback vectors. In such cases, we need to get the kind from 3009 // feedback vector slot since the handlers are used for both for StoreKeyed 3010 // and StoreInArrayLiteral kinds. 3011 FeedbackSlotKind kind = FeedbackSlotKind::kSetKeyedStrict; 3012 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 3013 if (!maybe_vector->IsUndefined()) { 3014 DCHECK(maybe_vector->IsFeedbackVector()); 3015 vector = Handle<FeedbackVector>::cast(maybe_vector); 3016 kind = vector->GetKind(vector_slot); 3017 } 3018 3019 // The elements store stubs miss into this function, but they are shared by 3020 // different ICs. 3021 // TODO(v8:12548): refactor DefineKeyedOwnIC as a subclass of KeyedStoreIC, 3022 // which can be called here. 3023 if (IsKeyedStoreICKind(kind) || IsDefineKeyedOwnICKind(kind)) { 3024 KeyedStoreIC ic(isolate, vector, vector_slot, kind); 3025 ic.UpdateState(receiver, key); 3026 RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); 3027 } else { 3028 DCHECK(IsStoreInArrayLiteralICKind(kind)); 3029 DCHECK(receiver->IsJSArray()); 3030 DCHECK(key->IsNumber()); 3031 StoreInArrayLiteralIC ic(isolate, vector, vector_slot); 3032 ic.UpdateState(receiver, key); 3033 RETURN_RESULT_OR_FAILURE( 3034 isolate, ic.Store(Handle<JSArray>::cast(receiver), key, value)); 3035 } 3036} 3037 3038RUNTIME_FUNCTION(Runtime_DefineKeyedOwnIC_Miss) { 3039 HandleScope scope(isolate); 3040 DCHECK_EQ(5, args.length()); 3041 // Runtime functions don't follow the IC's calling convention. 3042 Handle<Object> value = args.at(0); 3043 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 3044 Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); 3045 Handle<Object> receiver = args.at(3); 3046 Handle<Object> key = args.at(4); 3047 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 3048 3049 FeedbackSlotKind kind = FeedbackSlotKind::kDefineKeyedOwn; 3050 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 3051 if (!maybe_vector->IsUndefined()) { 3052 DCHECK(maybe_vector->IsFeedbackVector()); 3053 vector = Handle<FeedbackVector>::cast(maybe_vector); 3054 kind = vector->GetKind(vector_slot); 3055 DCHECK(IsDefineKeyedOwnICKind(kind)); 3056 } 3057 3058 // TODO(v8:12548): refactor DefineKeyedOwnIC as a subclass of KeyedStoreIC, 3059 // which can be called here. 3060 KeyedStoreIC ic(isolate, vector, vector_slot, kind); 3061 ic.UpdateState(receiver, key); 3062 RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value)); 3063} 3064 3065RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Miss) { 3066 HandleScope scope(isolate); 3067 DCHECK_EQ(5, args.length()); 3068 // Runtime functions don't follow the IC's calling convention. 3069 Handle<Object> value = args.at(0); 3070 Handle<TaggedIndex> slot = args.at<TaggedIndex>(1); 3071 Handle<HeapObject> maybe_vector = args.at<HeapObject>(2); 3072 Handle<Object> receiver = args.at(3); 3073 Handle<Object> key = args.at(4); 3074 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 3075 if (!maybe_vector->IsUndefined()) { 3076 DCHECK(maybe_vector->IsFeedbackVector()); 3077 vector = Handle<FeedbackVector>::cast(maybe_vector); 3078 } 3079 DCHECK(receiver->IsJSArray()); 3080 DCHECK(key->IsNumber()); 3081 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 3082 StoreInArrayLiteralIC ic(isolate, vector, vector_slot); 3083 RETURN_RESULT_OR_FAILURE( 3084 isolate, ic.Store(Handle<JSArray>::cast(receiver), key, value)); 3085} 3086 3087RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Slow) { 3088 HandleScope scope(isolate); 3089 DCHECK_EQ(3, args.length()); 3090 // Runtime functions don't follow the IC's calling convention. 3091 Handle<Object> value = args.at(0); 3092 Handle<Object> object = args.at(1); 3093 Handle<Object> key = args.at(2); 3094 RETURN_RESULT_OR_FAILURE( 3095 isolate, Runtime::SetObjectProperty(isolate, object, key, value, 3096 StoreOrigin::kMaybeKeyed)); 3097} 3098 3099RUNTIME_FUNCTION(Runtime_DefineKeyedOwnIC_Slow) { 3100 HandleScope scope(isolate); 3101 DCHECK_EQ(3, args.length()); 3102 // Runtime functions don't follow the IC's calling convention. 3103 Handle<Object> value = args.at(0); 3104 Handle<Object> object = args.at(1); 3105 Handle<Object> key = args.at(2); 3106 RETURN_RESULT_OR_FAILURE( 3107 isolate, Runtime::DefineObjectOwnProperty(isolate, object, key, value, 3108 StoreOrigin::kMaybeKeyed)); 3109} 3110 3111RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Slow) { 3112 HandleScope scope(isolate); 3113 DCHECK_EQ(3, args.length()); 3114 // Runtime functions don't follow the IC's calling convention. 3115 Handle<Object> value = args.at(0); 3116 Handle<Object> array = args.at(1); 3117 Handle<Object> index = args.at(2); 3118 StoreOwnElement(isolate, Handle<JSArray>::cast(array), index, value); 3119 return *value; 3120} 3121 3122RUNTIME_FUNCTION(Runtime_ElementsTransitionAndStoreIC_Miss) { 3123 HandleScope scope(isolate); 3124 DCHECK_EQ(6, args.length()); 3125 // Runtime functions don't follow the IC's calling convention. 3126 Handle<Object> object = args.at(0); 3127 Handle<Object> key = args.at(1); 3128 Handle<Object> value = args.at(2); 3129 Handle<Map> map = args.at<Map>(3); 3130 Handle<TaggedIndex> slot = args.at<TaggedIndex>(4); 3131 Handle<FeedbackVector> vector = args.at<FeedbackVector>(5); 3132 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 3133 FeedbackSlotKind kind = vector->GetKind(vector_slot); 3134 3135 if (object->IsJSObject()) { 3136 JSObject::TransitionElementsKind(Handle<JSObject>::cast(object), 3137 map->elements_kind()); 3138 } 3139 3140 if (IsStoreInArrayLiteralICKind(kind)) { 3141 StoreOwnElement(isolate, Handle<JSArray>::cast(object), key, value); 3142 return *value; 3143 } else { 3144 DCHECK(IsKeyedStoreICKind(kind) || IsSetNamedICKind(kind) || 3145 IsDefineKeyedOwnICKind(kind)); 3146 RETURN_RESULT_OR_FAILURE( 3147 isolate, 3148 IsDefineKeyedOwnICKind(kind) 3149 ? Runtime::DefineObjectOwnProperty(isolate, object, key, value, 3150 StoreOrigin::kMaybeKeyed) 3151 : Runtime::SetObjectProperty(isolate, object, key, value, 3152 StoreOrigin::kMaybeKeyed)); 3153 } 3154} 3155 3156static bool CanFastCloneObject(Handle<Map> map) { 3157 DisallowGarbageCollection no_gc; 3158 if (map->IsNullOrUndefinedMap()) return true; 3159 if (!map->IsJSObjectMap() || 3160 !IsSmiOrObjectElementsKind(map->elements_kind()) || 3161 !map->OnlyHasSimpleProperties()) { 3162 return false; 3163 } 3164 3165 DescriptorArray descriptors = map->instance_descriptors(); 3166 for (InternalIndex i : map->IterateOwnDescriptors()) { 3167 PropertyDetails details = descriptors.GetDetails(i); 3168 Name key = descriptors.GetKey(i); 3169 if (details.kind() != PropertyKind::kData || !details.IsEnumerable() || 3170 key.IsPrivateName()) { 3171 return false; 3172 } 3173 } 3174 3175 return true; 3176} 3177 3178static Handle<Map> FastCloneObjectMap(Isolate* isolate, Handle<Map> source_map, 3179 int flags) { 3180 SLOW_DCHECK(CanFastCloneObject(source_map)); 3181 Handle<JSFunction> constructor(isolate->native_context()->object_function(), 3182 isolate); 3183 DCHECK(constructor->has_initial_map()); 3184 Handle<Map> initial_map(constructor->initial_map(), isolate); 3185 Handle<Map> map = initial_map; 3186 3187 if (source_map->IsJSObjectMap() && source_map->GetInObjectProperties() != 3188 initial_map->GetInObjectProperties()) { 3189 int inobject_properties = source_map->GetInObjectProperties(); 3190 int instance_size = 3191 JSObject::kHeaderSize + kTaggedSize * inobject_properties; 3192 int unused = source_map->UnusedInObjectProperties(); 3193 DCHECK(instance_size <= JSObject::kMaxInstanceSize); 3194 map = Map::CopyInitialMap(isolate, map, instance_size, inobject_properties, 3195 unused); 3196 } 3197 3198 if (flags & ObjectLiteral::kHasNullPrototype) { 3199 if (map.is_identical_to(initial_map)) { 3200 map = Map::Copy(isolate, map, "ObjectWithNullProto"); 3201 } 3202 Map::SetPrototype(isolate, map, isolate->factory()->null_value()); 3203 } 3204 3205 if (source_map->NumberOfOwnDescriptors() == 0) { 3206 return map; 3207 } 3208 DCHECK(!source_map->IsNullOrUndefinedMap()); 3209 3210 if (map.is_identical_to(initial_map)) { 3211 map = Map::Copy(isolate, map, "InitializeClonedDescriptors"); 3212 } 3213 3214 Handle<DescriptorArray> source_descriptors( 3215 source_map->instance_descriptors(isolate), isolate); 3216 int size = source_map->NumberOfOwnDescriptors(); 3217 int slack = 0; 3218 Handle<DescriptorArray> descriptors = DescriptorArray::CopyForFastObjectClone( 3219 isolate, source_descriptors, size, slack); 3220 map->InitializeDescriptors(isolate, *descriptors); 3221 map->CopyUnusedPropertyFieldsAdjustedForInstanceSize(*source_map); 3222 3223 // Update bitfields 3224 map->set_may_have_interesting_symbols( 3225 source_map->may_have_interesting_symbols()); 3226 3227 return map; 3228} 3229 3230static MaybeHandle<JSObject> CloneObjectSlowPath(Isolate* isolate, 3231 Handle<Object> source, 3232 int flags) { 3233 Handle<JSObject> new_object; 3234 if (flags & ObjectLiteral::kHasNullPrototype) { 3235 new_object = isolate->factory()->NewJSObjectWithNullProto(); 3236 } else { 3237 Handle<JSFunction> constructor(isolate->native_context()->object_function(), 3238 isolate); 3239 new_object = isolate->factory()->NewJSObject(constructor); 3240 } 3241 3242 if (source->IsNullOrUndefined()) { 3243 return new_object; 3244 } 3245 3246 MAYBE_RETURN( 3247 JSReceiver::SetOrCopyDataProperties( 3248 isolate, new_object, source, 3249 PropertiesEnumerationMode::kPropertyAdditionOrder, nullptr, false), 3250 MaybeHandle<JSObject>()); 3251 return new_object; 3252} 3253 3254RUNTIME_FUNCTION(Runtime_CloneObjectIC_Miss) { 3255 HandleScope scope(isolate); 3256 DCHECK_EQ(4, args.length()); 3257 Handle<Object> source = args.at(0); 3258 int flags = args.smi_value_at(1); 3259 3260 if (!MigrateDeprecated(isolate, source)) { 3261 int index = args.tagged_index_value_at(2); 3262 FeedbackSlot slot = FeedbackVector::ToSlot(index); 3263 Handle<HeapObject> maybe_vector = args.at<HeapObject>(3); 3264 if (maybe_vector->IsFeedbackVector()) { 3265 FeedbackNexus nexus(Handle<FeedbackVector>::cast(maybe_vector), slot); 3266 if (!source->IsSmi() && !nexus.IsMegamorphic()) { 3267 Handle<Map> source_map(Handle<HeapObject>::cast(source)->map(), 3268 isolate); 3269 if (CanFastCloneObject(source_map)) { 3270 Handle<Map> target_map = 3271 FastCloneObjectMap(isolate, source_map, flags); 3272 nexus.ConfigureCloneObject(source_map, target_map); 3273 return *target_map; 3274 } 3275 3276 nexus.ConfigureMegamorphic(); 3277 } 3278 } 3279 } 3280 3281 RETURN_RESULT_OR_FAILURE(isolate, 3282 CloneObjectSlowPath(isolate, source, flags)); 3283} 3284 3285RUNTIME_FUNCTION(Runtime_StoreCallbackProperty) { 3286 Handle<JSObject> receiver = args.at<JSObject>(0); 3287 Handle<JSObject> holder = args.at<JSObject>(1); 3288 Handle<AccessorInfo> info = args.at<AccessorInfo>(2); 3289 Handle<Name> name = args.at<Name>(3); 3290 Handle<Object> value = args.at(4); 3291 HandleScope scope(isolate); 3292 3293#ifdef V8_RUNTIME_CALL_STATS 3294 if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled())) { 3295 RETURN_RESULT_OR_FAILURE( 3296 isolate, Runtime::SetObjectProperty(isolate, receiver, name, value, 3297 StoreOrigin::kMaybeKeyed)); 3298 } 3299#endif 3300 3301 DCHECK(info->IsCompatibleReceiver(*receiver)); 3302 3303 PropertyCallbackArguments arguments(isolate, info->data(), *receiver, *holder, 3304 Nothing<ShouldThrow>()); 3305 arguments.CallAccessorSetter(info, name, value); 3306 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); 3307 return *value; 3308} 3309 3310/** 3311 * Loads a property with an interceptor performing post interceptor 3312 * lookup if interceptor failed. 3313 */ 3314RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { 3315 HandleScope scope(isolate); 3316 DCHECK_EQ(5, args.length()); 3317 Handle<Name> name = args.at<Name>(0); 3318 Handle<Object> receiver = args.at(1); 3319 Handle<JSObject> holder = args.at<JSObject>(2); 3320 3321 if (!receiver->IsJSReceiver()) { 3322 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( 3323 isolate, receiver, Object::ConvertReceiver(isolate, receiver)); 3324 } 3325 3326 Handle<InterceptorInfo> interceptor(holder->GetNamedInterceptor(), isolate); 3327 PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, 3328 *holder, Just(kDontThrow)); 3329 Handle<Object> result = arguments.CallNamedGetter(interceptor, name); 3330 3331 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); 3332 3333 if (!result.is_null()) return *result; 3334 3335 LookupIterator it(isolate, receiver, name, holder); 3336 // Skip any lookup work until we hit the (possibly non-masking) interceptor. 3337 while (it.state() != LookupIterator::INTERCEPTOR || 3338 !it.GetHolder<JSObject>().is_identical_to(holder)) { 3339 DCHECK(it.state() != LookupIterator::ACCESS_CHECK || it.HasAccess()); 3340 it.Next(); 3341 } 3342 // Skip past the interceptor. 3343 it.Next(); 3344 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, Object::GetProperty(&it)); 3345 3346 if (it.IsFound()) return *result; 3347 3348 Handle<TaggedIndex> slot = args.at<TaggedIndex>(3); 3349 Handle<FeedbackVector> vector = args.at<FeedbackVector>(4); 3350 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 3351 FeedbackSlotKind slot_kind = vector->GetKind(vector_slot); 3352 // It could actually be any kind of load IC slot here but the predicate 3353 // handles all the cases properly. 3354 if (!LoadIC::ShouldThrowReferenceError(slot_kind)) { 3355 return ReadOnlyRoots(isolate).undefined_value(); 3356 } 3357 3358 // Throw a reference error. 3359 THROW_NEW_ERROR_RETURN_FAILURE( 3360 isolate, NewReferenceError(MessageTemplate::kNotDefined, it.name())); 3361} 3362 3363RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { 3364 HandleScope scope(isolate); 3365 DCHECK_EQ(3, args.length()); 3366 // Runtime functions don't follow the IC's calling convention. 3367 Handle<Object> value = args.at(0); 3368 Handle<JSObject> receiver = args.at<JSObject>(1); 3369 Handle<Name> name = args.at<Name>(2); 3370 3371 // TODO(ishell): Cache interceptor_holder in the store handler like we do 3372 // for LoadHandler::kInterceptor case. 3373 Handle<JSObject> interceptor_holder = receiver; 3374 if (receiver->IsJSGlobalProxy() && 3375 (!receiver->HasNamedInterceptor() || 3376 receiver->GetNamedInterceptor().non_masking())) { 3377 interceptor_holder = 3378 handle(JSObject::cast(receiver->map().prototype()), isolate); 3379 } 3380 DCHECK(interceptor_holder->HasNamedInterceptor()); 3381 Handle<InterceptorInfo> interceptor(interceptor_holder->GetNamedInterceptor(), 3382 isolate); 3383 3384 DCHECK(!interceptor->non_masking()); 3385 PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, 3386 *receiver, Just(kDontThrow)); 3387 3388 Handle<Object> result = arguments.CallNamedSetter(interceptor, name, value); 3389 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); 3390 if (!result.is_null()) return *value; 3391 3392 LookupIterator it(isolate, receiver, name, receiver); 3393 // Skip past any access check on the receiver. 3394 if (it.state() == LookupIterator::ACCESS_CHECK) { 3395 DCHECK(it.HasAccess()); 3396 it.Next(); 3397 } 3398 // Skip past the interceptor on the receiver. 3399 DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state()); 3400 it.Next(); 3401 3402 MAYBE_RETURN(Object::SetProperty(&it, value, StoreOrigin::kNamed), 3403 ReadOnlyRoots(isolate).exception()); 3404 return *value; 3405} 3406 3407RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor) { 3408 // TODO(verwaest): This should probably get the holder and receiver as input. 3409 HandleScope scope(isolate); 3410 Handle<JSObject> receiver = args.at<JSObject>(0); 3411 DCHECK_GE(args.smi_value_at(1), 0); 3412 uint32_t index = args.smi_value_at(1); 3413 3414 Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(), 3415 isolate); 3416 PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, 3417 *receiver, Just(kDontThrow)); 3418 Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); 3419 3420 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); 3421 3422 if (result.is_null()) { 3423 LookupIterator it(isolate, receiver, index, receiver); 3424 DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state()); 3425 it.Next(); 3426 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, 3427 Object::GetProperty(&it)); 3428 } 3429 3430 return *result; 3431} 3432 3433RUNTIME_FUNCTION(Runtime_KeyedHasIC_Miss) { 3434 HandleScope scope(isolate); 3435 DCHECK_EQ(4, args.length()); 3436 // Runtime functions don't follow the IC's calling convention. 3437 Handle<Object> receiver = args.at(0); 3438 Handle<Object> key = args.at(1); 3439 Handle<TaggedIndex> slot = args.at<TaggedIndex>(2); 3440 Handle<HeapObject> maybe_vector = args.at<HeapObject>(3); 3441 3442 Handle<FeedbackVector> vector = Handle<FeedbackVector>(); 3443 if (!maybe_vector->IsUndefined()) { 3444 DCHECK(maybe_vector->IsFeedbackVector()); 3445 vector = Handle<FeedbackVector>::cast(maybe_vector); 3446 } 3447 FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); 3448 KeyedLoadIC ic(isolate, vector, vector_slot, FeedbackSlotKind::kHasKeyed); 3449 ic.UpdateState(receiver, key); 3450 RETURN_RESULT_OR_FAILURE(isolate, ic.Load(receiver, key)); 3451} 3452 3453RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor) { 3454 HandleScope scope(isolate); 3455 Handle<JSObject> receiver = args.at<JSObject>(0); 3456 DCHECK_GE(args.smi_value_at(1), 0); 3457 uint32_t index = args.smi_value_at(1); 3458 3459 Handle<InterceptorInfo> interceptor(receiver->GetIndexedInterceptor(), 3460 isolate); 3461 PropertyCallbackArguments arguments(isolate, interceptor->data(), *receiver, 3462 *receiver, Just(kDontThrow)); 3463 3464 if (!interceptor->query().IsUndefined(isolate)) { 3465 Handle<Object> result = arguments.CallIndexedQuery(interceptor, index); 3466 if (!result.is_null()) { 3467 int32_t value; 3468 CHECK(result->ToInt32(&value)); 3469 return value == ABSENT ? ReadOnlyRoots(isolate).false_value() 3470 : ReadOnlyRoots(isolate).true_value(); 3471 } 3472 } else if (!interceptor->getter().IsUndefined(isolate)) { 3473 Handle<Object> result = arguments.CallIndexedGetter(interceptor, index); 3474 if (!result.is_null()) { 3475 return ReadOnlyRoots(isolate).true_value(); 3476 } 3477 } 3478 3479 LookupIterator it(isolate, receiver, index, receiver); 3480 DCHECK_EQ(LookupIterator::INTERCEPTOR, it.state()); 3481 it.Next(); 3482 Maybe<bool> maybe = JSReceiver::HasProperty(&it); 3483 if (maybe.IsNothing()) return ReadOnlyRoots(isolate).exception(); 3484 return maybe.FromJust() ? ReadOnlyRoots(isolate).true_value() 3485 : ReadOnlyRoots(isolate).false_value(); 3486} 3487 3488} // namespace internal 3489} // namespace v8 3490