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