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
51 namespace v8 {
52 namespace internal {
53
54 // Aliases to avoid having to repeat the class.
55 // With C++20 we can use "using" to introduce scoped enums.
56 constexpr InlineCacheState NO_FEEDBACK = InlineCacheState::NO_FEEDBACK;
57 constexpr InlineCacheState UNINITIALIZED = InlineCacheState::UNINITIALIZED;
58 constexpr InlineCacheState MONOMORPHIC = InlineCacheState::MONOMORPHIC;
59 constexpr InlineCacheState RECOMPUTE_HANDLER =
60 InlineCacheState::RECOMPUTE_HANDLER;
61 constexpr InlineCacheState POLYMORPHIC = InlineCacheState::POLYMORPHIC;
62 constexpr InlineCacheState MEGAMORPHIC = InlineCacheState::MEGAMORPHIC;
63 constexpr InlineCacheState MEGADOM = InlineCacheState::MEGADOM;
64 constexpr InlineCacheState GENERIC = InlineCacheState::GENERIC;
65
TransitionMarkFromState(IC::State state)66 char 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
88 namespace {
89
GetModifier(KeyedAccessLoadMode mode)90 const char* GetModifier(KeyedAccessLoadMode mode) {
91 if (mode == LOAD_IGNORE_OUT_OF_BOUNDS) return ".IGNORE_OOB";
92 return "";
93 }
94
GetModifier(KeyedAccessStoreMode mode)95 const 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
TraceIC(const char * type,Handle<Object> name)111 void 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
TraceIC(const char * type,Handle<Object> name,State old_state,State new_state)118 void 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
IC(Isolate * isolate,Handle<FeedbackVector> vector,FeedbackSlot slot,FeedbackSlotKind kind)193 IC::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
LookupForRead(LookupIterator * it,bool is_has_property)206 static 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
ShouldRecomputeHandler(Handle<String> name)241 bool 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
RecomputeHandlerForName(Handle<Object> name)269 bool 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
UpdateState(Handle<Object> lookup_start_object,Handle<Object> name)280 void 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
TypeError(MessageTemplate index,Handle<Object> object,Handle<Object> key)295 MaybeHandle<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
ReferenceError(Handle<Name> name)301 MaybeHandle<Object> IC::ReferenceError(Handle<Name> name) {
302 HandleScope scope(isolate());
303 THROW_NEW_ERROR(
304 isolate(), NewReferenceError(MessageTemplate::kNotDefined, name), Object);
305 }
306
OnFeedbackChanged(const char * reason)307 void 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
OnFeedbackChanged(Isolate * isolate,FeedbackVector vector,FeedbackSlot slot,const char * reason)315 void 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
352 namespace {
353
MigrateDeprecated(Isolate * isolate,Handle<Object> object)354 bool 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
ConfigureVectorState(IC::State new_state,Handle<Object> key)364 bool 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
ConfigureVectorState(Handle<Name> name,Handle<Map> map,Handle<Object> handler)376 void IC::ConfigureVectorState(Handle<Name> name, Handle<Map> map,
377 Handle<Object> handler) {
378 ConfigureVectorState(name, map, MaybeObjectHandle(handler));
379 }
380
ConfigureVectorState(Handle<Name> name,Handle<Map> map,const MaybeObjectHandle & handler)381 void 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
ConfigureVectorState(Handle<Name> name,MapHandles const & maps,MaybeObjectHandles * handlers)394 void 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
ConfigureVectorState(Handle<Name> name,std::vector<MapAndHandler> const & maps_and_handlers)405 void 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
Load(Handle<Object> object,Handle<Name> name,bool update_feedback,Handle<Object> receiver)415 MaybeHandle<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
Load(Handle<Name> name,bool update_feedback)523 MaybeHandle<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
AddOneReceiverMapIfMissing(MapHandles * receiver_maps,Handle<Map> new_receiver_map)575 static 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
AddOneReceiverMapIfMissing(std::vector<MapAndHandler> * receiver_maps_and_handlers,Handle<Map> new_receiver_map)587 static 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
UpdateMegaDOMIC(const MaybeObjectHandle & handler,Handle<Name> name)603 bool 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
UpdatePolymorphicIC(Handle<Name> name,const MaybeObjectHandle & handler)652 bool 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
UpdateMonomorphicIC(const MaybeObjectHandle & handler,Handle<Name> name)735 void IC::UpdateMonomorphicIC(const MaybeObjectHandle& handler,
736 Handle<Name> name) {
737 DCHECK(IsHandler(*handler));
738 ConfigureVectorState(name, lookup_start_object_map(), handler);
739 }
740
CopyICToMegamorphicCache(Handle<Name> name)741 void 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
IsTransitionOfMonomorphicTarget(Map source_map,Map target_map)749 bool 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
SetCache(Handle<Name> name,Handle<Object> handler)766 void IC::SetCache(Handle<Name> name, Handle<Object> handler) {
767 SetCache(name, MaybeObjectHandle(handler));
768 }
769
SetCache(Handle<Name> name,const MaybeObjectHandle & handler)770 void 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
UpdateCaches(LookupIterator * lookup)807 void 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
stub_cache()845 StubCache* 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
UpdateMegamorphicCache(Handle<Map> map,Handle<Name> name,const MaybeObjectHandle & handler)858 void 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
865 namespace {
866
867 #if V8_ENABLE_WEBASSEMBLY
868
GetWasmValueType(wasm::ValueType type)869 inline 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
MakeLoadWasmStructFieldHandler(Isolate * isolate,Handle<JSReceiver> holder,LookupIterator * lookup)896 Handle<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
CodeHandler(Builtin builtin)927 Handle<Object> IC::CodeHandler(Builtin builtin) {
928 return MakeCodeHandler(isolate(), builtin);
929 }
930
ComputeHandler(LookupIterator * lookup)931 Handle<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
CanChangeToAllowOutOfBounds(Handle<Map> receiver_map)1227 bool 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
UpdateLoadElement(Handle<HeapObject> receiver,KeyedAccessLoadMode load_mode)1233 void 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
1315 namespace {
1316
AllowConvertHoleElementToUndefined(Isolate * isolate,Handle<Map> receiver_map)1317 bool 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
LoadElementHandler(Handle<Map> receiver_map,KeyedAccessLoadMode load_mode)1349 Handle<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
LoadElementPolymorphicHandlers(MapHandles * receiver_maps,MaybeObjectHandles * handlers,KeyedAccessLoadMode load_mode)1411 void 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
1437 namespace {
1438
1439 enum KeyType { kIntPtr, kName, kBailout };
1440
1441 // The cases where kIntPtr is returned must match what
1442 // CodeStubAssembler::TryToIntptr can handle!
TryConvertKey(Handle<Object> key,Isolate * isolate,intptr_t * index_out,Handle<Name> * name_out)1443 KeyType 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
IntPtrKeyToSize(intptr_t index,Handle<HeapObject> receiver,size_t * out)1479 bool 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
CanCache(Handle<Object> receiver,InlineCacheState state)1505 bool 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
IsOutOfBoundsAccess(Handle<Object> receiver,size_t index)1511 bool 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
GetLoadMode(Isolate * isolate,Handle<Object> receiver,size_t index)1527 KeyedAccessLoadMode 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
RuntimeLoad(Handle<Object> object,Handle<Object> key)1542 MaybeHandle<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
Load(Handle<Object> object,Handle<Object> key)1559 MaybeHandle<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
LookupForWrite(LookupIterator * it,Handle<Object> value,StoreOrigin store_origin)1595 bool 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
Store(Handle<Name> name,Handle<Object> value)1686 MaybeHandle<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
1739 namespace {
DefineOwnDataProperty(LookupIterator * it,LookupIterator::State original_state,Handle<Object> value,Maybe<ShouldThrow> should_throw,StoreOrigin store_origin)1740 Maybe<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
Store(Handle<Object> object,Handle<Name> name,Handle<Object> value,StoreOrigin store_origin)1803 MaybeHandle<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
UpdateCaches(LookupIterator * lookup,Handle<Object> value,StoreOrigin store_origin)1913 void 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
ComputeHandler(LookupIterator * lookup)1939 MaybeObjectHandle 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
UpdateStoreElement(Handle<Map> receiver_map,KeyedAccessStoreMode store_mode,Handle<Map> new_receiver_map)2198 void 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
StoreElementHandler(Handle<Map> receiver_map,KeyedAccessStoreMode store_mode,MaybeHandle<Object> prev_validity_cell)2335 Handle<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
StoreElementPolymorphicHandlers(std::vector<MapAndHandler> * receiver_maps_and_handlers,KeyedAccessStoreMode store_mode)2393 void 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
2457 namespace {
2458
MayHaveTypedArrayInPrototypeChain(Handle<JSObject> object)2459 bool 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
GetStoreMode(Handle<JSObject> receiver,size_t index)2469 KeyedAccessStoreMode 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
Store(Handle<Object> object,Handle<Object> key,Handle<Object> value)2488 MaybeHandle<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
2621 namespace {
StoreOwnElement(Isolate * isolate,Handle<JSArray> array,Handle<Object> index,Handle<Object> value)2622 Maybe<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
Store(Handle<JSArray> array,Handle<Object> index,Handle<Object> value)2635 MaybeHandle<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 //
RUNTIME_FUNCTION(Runtime_LoadIC_Miss)2679 RUNTIME_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
RUNTIME_FUNCTION(Runtime_LoadNoFeedbackIC_Miss)2713 RUNTIME_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
RUNTIME_FUNCTION(Runtime_LoadWithReceiverNoFeedbackIC_Miss)2731 RUNTIME_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
RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Miss)2746 RUNTIME_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
RUNTIME_FUNCTION(Runtime_LoadGlobalIC_Slow)2775 RUNTIME_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
RUNTIME_FUNCTION(Runtime_LoadWithReceiverIC_Miss)2791 RUNTIME_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
RUNTIME_FUNCTION(Runtime_KeyedLoadIC_Miss)2808 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StoreIC_Miss)2828 RUNTIME_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
RUNTIME_FUNCTION(Runtime_DefineNamedOwnIC_Miss)2858 RUNTIME_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
RUNTIME_FUNCTION(Runtime_DefineNamedOwnIC_Slow)2889 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Miss)2911 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StoreGlobalICNoFeedback_Miss)2928 RUNTIME_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.
RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Slow)2943 RUNTIME_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
RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss)2994 RUNTIME_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
RUNTIME_FUNCTION(Runtime_DefineKeyedOwnIC_Miss)3038 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Miss)3065 RUNTIME_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
RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Slow)3087 RUNTIME_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
RUNTIME_FUNCTION(Runtime_DefineKeyedOwnIC_Slow)3099 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Slow)3111 RUNTIME_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
RUNTIME_FUNCTION(Runtime_ElementsTransitionAndStoreIC_Miss)3122 RUNTIME_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
CanFastCloneObject(Handle<Map> map)3156 static 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
FastCloneObjectMap(Isolate * isolate,Handle<Map> source_map,int flags)3178 static 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
CloneObjectSlowPath(Isolate * isolate,Handle<Object> source,int flags)3230 static 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
RUNTIME_FUNCTION(Runtime_CloneObjectIC_Miss)3254 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StoreCallbackProperty)3285 RUNTIME_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 */
RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor)3314 RUNTIME_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
RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor)3363 RUNTIME_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
RUNTIME_FUNCTION(Runtime_LoadElementWithInterceptor)3407 RUNTIME_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
RUNTIME_FUNCTION(Runtime_KeyedHasIC_Miss)3433 RUNTIME_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
RUNTIME_FUNCTION(Runtime_HasElementWithInterceptor)3453 RUNTIME_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