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