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/v8.h"
6
7 #include "src/accessors.h"
8 #include "src/api.h"
9 #include "src/arguments.h"
10 #include "src/base/bits.h"
11 #include "src/codegen.h"
12 #include "src/conversions.h"
13 #include "src/execution.h"
14 #include "src/ic/call-optimization.h"
15 #include "src/ic/handler-compiler.h"
16 #include "src/ic/ic-inl.h"
17 #include "src/ic/ic-compiler.h"
18 #include "src/ic/stub-cache.h"
19 #include "src/prototype.h"
20 #include "src/runtime.h"
21
22 namespace v8 {
23 namespace internal {
24
TransitionMarkFromState(IC::State state)25 char IC::TransitionMarkFromState(IC::State state) {
26 switch (state) {
27 case UNINITIALIZED:
28 return '0';
29 case PREMONOMORPHIC:
30 return '.';
31 case MONOMORPHIC:
32 return '1';
33 case PROTOTYPE_FAILURE:
34 return '^';
35 case POLYMORPHIC:
36 return 'P';
37 case MEGAMORPHIC:
38 return 'N';
39 case GENERIC:
40 return 'G';
41
42 // We never see the debugger states here, because the state is
43 // computed from the original code - not the patched code. Let
44 // these cases fall through to the unreachable code below.
45 case DEBUG_STUB:
46 break;
47 // Type-vector-based ICs resolve state to one of the above.
48 case DEFAULT:
49 break;
50 }
51 UNREACHABLE();
52 return 0;
53 }
54
55
GetTransitionMarkModifier(KeyedAccessStoreMode mode)56 const char* GetTransitionMarkModifier(KeyedAccessStoreMode mode) {
57 if (mode == STORE_NO_TRANSITION_HANDLE_COW) return ".COW";
58 if (mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
59 return ".IGNORE_OOB";
60 }
61 if (IsGrowStoreMode(mode)) return ".GROW";
62 return "";
63 }
64
65
66 #ifdef DEBUG
67
68 #define TRACE_GENERIC_IC(isolate, type, reason) \
69 do { \
70 if (FLAG_trace_ic) { \
71 PrintF("[%s patching generic stub in ", type); \
72 JavaScriptFrame::PrintTop(isolate, stdout, false, true); \
73 PrintF(" (%s)]\n", reason); \
74 } \
75 } while (false)
76
77 #else
78
79 #define TRACE_GENERIC_IC(isolate, type, reason) \
80 do { \
81 if (FLAG_trace_ic) { \
82 PrintF("[%s patching generic stub in ", type); \
83 PrintF("(see below) (%s)]\n", reason); \
84 } \
85 } while (false)
86
87 #endif // DEBUG
88
89
TraceIC(const char * type,Handle<Object> name)90 void IC::TraceIC(const char* type, Handle<Object> name) {
91 if (FLAG_trace_ic) {
92 Code* new_target = raw_target();
93 State new_state = new_target->ic_state();
94 TraceIC(type, name, state(), new_state);
95 }
96 }
97
98
TraceIC(const char * type,Handle<Object> name,State old_state,State new_state)99 void IC::TraceIC(const char* type, Handle<Object> name, State old_state,
100 State new_state) {
101 if (FLAG_trace_ic) {
102 Code* new_target = raw_target();
103 PrintF("[%s%s in ", new_target->is_keyed_stub() ? "Keyed" : "", type);
104
105 // TODO(jkummerow): Add support for "apply". The logic is roughly:
106 // marker = [fp_ + kMarkerOffset];
107 // if marker is smi and marker.value == INTERNAL and
108 // the frame's code == builtin(Builtins::kFunctionApply):
109 // then print "apply from" and advance one frame
110
111 Object* maybe_function =
112 Memory::Object_at(fp_ + JavaScriptFrameConstants::kFunctionOffset);
113 if (maybe_function->IsJSFunction()) {
114 JSFunction* function = JSFunction::cast(maybe_function);
115 JavaScriptFrame::PrintFunctionAndOffset(function, function->code(), pc(),
116 stdout, true);
117 }
118
119 ExtraICState extra_state = new_target->extra_ic_state();
120 const char* modifier = "";
121 if (new_target->kind() == Code::KEYED_STORE_IC) {
122 modifier = GetTransitionMarkModifier(
123 KeyedStoreIC::GetKeyedAccessStoreMode(extra_state));
124 }
125 PrintF(" (%c->%c%s)", TransitionMarkFromState(old_state),
126 TransitionMarkFromState(new_state), modifier);
127 #ifdef OBJECT_PRINT
128 OFStream os(stdout);
129 name->Print(os);
130 #else
131 name->ShortPrint(stdout);
132 #endif
133 PrintF("]\n");
134 }
135 }
136
137 #define TRACE_IC(type, name) TraceIC(type, name)
138 #define TRACE_VECTOR_IC(type, name, old_state, new_state) \
139 TraceIC(type, name, old_state, new_state)
140
IC(FrameDepth depth,Isolate * isolate)141 IC::IC(FrameDepth depth, Isolate* isolate)
142 : isolate_(isolate), target_set_(false), target_maps_set_(false) {
143 // To improve the performance of the (much used) IC code, we unfold a few
144 // levels of the stack frame iteration code. This yields a ~35% speedup when
145 // running DeltaBlue and a ~25% speedup of gbemu with the '--nouse-ic' flag.
146 const Address entry = Isolate::c_entry_fp(isolate->thread_local_top());
147 Address constant_pool = NULL;
148 if (FLAG_enable_ool_constant_pool) {
149 constant_pool =
150 Memory::Address_at(entry + ExitFrameConstants::kConstantPoolOffset);
151 }
152 Address* pc_address =
153 reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset);
154 Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
155 // If there's another JavaScript frame on the stack or a
156 // StubFailureTrampoline, we need to look one frame further down the stack to
157 // find the frame pointer and the return address stack slot.
158 if (depth == EXTRA_CALL_FRAME) {
159 if (FLAG_enable_ool_constant_pool) {
160 constant_pool =
161 Memory::Address_at(fp + StandardFrameConstants::kConstantPoolOffset);
162 }
163 const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset;
164 pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset);
165 fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
166 }
167 #ifdef DEBUG
168 StackFrameIterator it(isolate);
169 for (int i = 0; i < depth + 1; i++) it.Advance();
170 StackFrame* frame = it.frame();
171 DCHECK(fp == frame->fp() && pc_address == frame->pc_address());
172 #endif
173 fp_ = fp;
174 if (FLAG_enable_ool_constant_pool) {
175 raw_constant_pool_ = handle(
176 ConstantPoolArray::cast(reinterpret_cast<Object*>(constant_pool)),
177 isolate);
178 }
179 pc_address_ = StackFrame::ResolveReturnAddressLocation(pc_address);
180 target_ = handle(raw_target(), isolate);
181 state_ = target_->ic_state();
182 kind_ = target_->kind();
183 extra_ic_state_ = target_->extra_ic_state();
184 }
185
186
GetSharedFunctionInfo() const187 SharedFunctionInfo* IC::GetSharedFunctionInfo() const {
188 // Compute the JavaScript frame for the frame pointer of this IC
189 // structure. We need this to be able to find the function
190 // corresponding to the frame.
191 StackFrameIterator it(isolate());
192 while (it.frame()->fp() != this->fp()) it.Advance();
193 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
194 // Find the function on the stack and both the active code for the
195 // function and the original code.
196 JSFunction* function = frame->function();
197 return function->shared();
198 }
199
200
GetCode() const201 Code* IC::GetCode() const {
202 HandleScope scope(isolate());
203 Handle<SharedFunctionInfo> shared(GetSharedFunctionInfo(), isolate());
204 Code* code = shared->code();
205 return code;
206 }
207
208
GetOriginalCode() const209 Code* IC::GetOriginalCode() const {
210 HandleScope scope(isolate());
211 Handle<SharedFunctionInfo> shared(GetSharedFunctionInfo(), isolate());
212 DCHECK(Debug::HasDebugInfo(shared));
213 Code* original_code = Debug::GetDebugInfo(shared)->original_code();
214 DCHECK(original_code->IsCode());
215 return original_code;
216 }
217
218
LookupForRead(LookupIterator * it)219 static void LookupForRead(LookupIterator* it) {
220 for (; it->IsFound(); it->Next()) {
221 switch (it->state()) {
222 case LookupIterator::NOT_FOUND:
223 case LookupIterator::TRANSITION:
224 UNREACHABLE();
225 case LookupIterator::JSPROXY:
226 return;
227 case LookupIterator::INTERCEPTOR: {
228 // If there is a getter, return; otherwise loop to perform the lookup.
229 Handle<JSObject> holder = it->GetHolder<JSObject>();
230 if (!holder->GetNamedInterceptor()->getter()->IsUndefined()) {
231 return;
232 }
233 break;
234 }
235 case LookupIterator::ACCESS_CHECK:
236 // PropertyHandlerCompiler::CheckPrototypes() knows how to emit
237 // access checks for global proxies.
238 if (it->GetHolder<JSObject>()->IsJSGlobalProxy() &&
239 it->HasAccess(v8::ACCESS_GET)) {
240 break;
241 }
242 return;
243 case LookupIterator::ACCESSOR:
244 case LookupIterator::DATA:
245 return;
246 }
247 }
248 }
249
250
TryRemoveInvalidPrototypeDependentStub(Handle<Object> receiver,Handle<String> name)251 bool IC::TryRemoveInvalidPrototypeDependentStub(Handle<Object> receiver,
252 Handle<String> name) {
253 if (!IsNameCompatibleWithPrototypeFailure(name)) return false;
254 Handle<Map> receiver_map = TypeToMap(*receiver_type(), isolate());
255 maybe_handler_ = target()->FindHandlerForMap(*receiver_map);
256
257 // The current map wasn't handled yet. There's no reason to stay monomorphic,
258 // *unless* we're moving from a deprecated map to its replacement, or
259 // to a more general elements kind.
260 // TODO(verwaest): Check if the current map is actually what the old map
261 // would transition to.
262 if (maybe_handler_.is_null()) {
263 if (!receiver_map->IsJSObjectMap()) return false;
264 Map* first_map = FirstTargetMap();
265 if (first_map == NULL) return false;
266 Handle<Map> old_map(first_map);
267 if (old_map->is_deprecated()) return true;
268 if (IsMoreGeneralElementsKindTransition(old_map->elements_kind(),
269 receiver_map->elements_kind())) {
270 return true;
271 }
272 return false;
273 }
274
275 CacheHolderFlag flag;
276 Handle<Map> ic_holder_map(
277 GetICCacheHolder(*receiver_type(), isolate(), &flag));
278
279 DCHECK(flag != kCacheOnReceiver || receiver->IsJSObject());
280 DCHECK(flag != kCacheOnPrototype || !receiver->IsJSReceiver());
281 DCHECK(flag != kCacheOnPrototypeReceiverIsDictionary);
282
283 if (state() == MONOMORPHIC) {
284 int index = ic_holder_map->IndexInCodeCache(*name, *target());
285 if (index >= 0) {
286 ic_holder_map->RemoveFromCodeCache(*name, *target(), index);
287 }
288 }
289
290 if (receiver->IsGlobalObject()) {
291 Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
292 LookupIterator it(global, name, LookupIterator::OWN_SKIP_INTERCEPTOR);
293 if (it.state() == LookupIterator::ACCESS_CHECK) return false;
294 if (!it.IsFound()) return false;
295 Handle<PropertyCell> cell = it.GetPropertyCell();
296 return cell->type()->IsConstant();
297 }
298
299 return true;
300 }
301
302
IsNameCompatibleWithPrototypeFailure(Handle<Object> name)303 bool IC::IsNameCompatibleWithPrototypeFailure(Handle<Object> name) {
304 if (target()->is_keyed_stub()) {
305 // Determine whether the failure is due to a name failure.
306 if (!name->IsName()) return false;
307 Name* stub_name = target()->FindFirstName();
308 if (*name != stub_name) return false;
309 }
310
311 return true;
312 }
313
314
UpdateState(Handle<Object> receiver,Handle<Object> name)315 void IC::UpdateState(Handle<Object> receiver, Handle<Object> name) {
316 update_receiver_type(receiver);
317 if (!name->IsString()) return;
318 if (state() != MONOMORPHIC && state() != POLYMORPHIC) return;
319 if (receiver->IsUndefined() || receiver->IsNull()) return;
320
321 // Remove the target from the code cache if it became invalid
322 // because of changes in the prototype chain to avoid hitting it
323 // again.
324 if (TryRemoveInvalidPrototypeDependentStub(receiver,
325 Handle<String>::cast(name))) {
326 MarkPrototypeFailure(name);
327 return;
328 }
329
330 // The builtins object is special. It only changes when JavaScript
331 // builtins are loaded lazily. It is important to keep inline
332 // caches for the builtins object monomorphic. Therefore, if we get
333 // an inline cache miss for the builtins object after lazily loading
334 // JavaScript builtins, we return uninitialized as the state to
335 // force the inline cache back to monomorphic state.
336 if (receiver->IsJSBuiltinsObject()) state_ = UNINITIALIZED;
337 }
338
339
TypeError(const char * type,Handle<Object> object,Handle<Object> key)340 MaybeHandle<Object> IC::TypeError(const char* type, Handle<Object> object,
341 Handle<Object> key) {
342 HandleScope scope(isolate());
343 Handle<Object> args[2] = {key, object};
344 THROW_NEW_ERROR(isolate(), NewTypeError(type, HandleVector(args, 2)), Object);
345 }
346
347
ReferenceError(const char * type,Handle<Name> name)348 MaybeHandle<Object> IC::ReferenceError(const char* type, Handle<Name> name) {
349 HandleScope scope(isolate());
350 THROW_NEW_ERROR(isolate(), NewReferenceError(type, HandleVector(&name, 1)),
351 Object);
352 }
353
354
ComputeTypeInfoCountDelta(IC::State old_state,IC::State new_state,int * polymorphic_delta,int * generic_delta)355 static void ComputeTypeInfoCountDelta(IC::State old_state, IC::State new_state,
356 int* polymorphic_delta,
357 int* generic_delta) {
358 switch (old_state) {
359 case UNINITIALIZED:
360 case PREMONOMORPHIC:
361 if (new_state == UNINITIALIZED || new_state == PREMONOMORPHIC) break;
362 if (new_state == MONOMORPHIC || new_state == POLYMORPHIC) {
363 *polymorphic_delta = 1;
364 } else if (new_state == MEGAMORPHIC || new_state == GENERIC) {
365 *generic_delta = 1;
366 }
367 break;
368 case MONOMORPHIC:
369 case POLYMORPHIC:
370 if (new_state == MONOMORPHIC || new_state == POLYMORPHIC) break;
371 *polymorphic_delta = -1;
372 if (new_state == MEGAMORPHIC || new_state == GENERIC) {
373 *generic_delta = 1;
374 }
375 break;
376 case MEGAMORPHIC:
377 case GENERIC:
378 if (new_state == MEGAMORPHIC || new_state == GENERIC) break;
379 *generic_delta = -1;
380 if (new_state == MONOMORPHIC || new_state == POLYMORPHIC) {
381 *polymorphic_delta = 1;
382 }
383 break;
384 case PROTOTYPE_FAILURE:
385 case DEBUG_STUB:
386 case DEFAULT:
387 UNREACHABLE();
388 }
389 }
390
391
OnTypeFeedbackChanged(Isolate * isolate,Address address,State old_state,State new_state,bool target_remains_ic_stub)392 void IC::OnTypeFeedbackChanged(Isolate* isolate, Address address,
393 State old_state, State new_state,
394 bool target_remains_ic_stub) {
395 Code* host =
396 isolate->inner_pointer_to_code_cache()->GetCacheEntry(address)->code;
397 if (host->kind() != Code::FUNCTION) return;
398
399 if (FLAG_type_info_threshold > 0 && target_remains_ic_stub &&
400 // Not all Code objects have TypeFeedbackInfo.
401 host->type_feedback_info()->IsTypeFeedbackInfo()) {
402 int polymorphic_delta = 0; // "Polymorphic" here includes monomorphic.
403 int generic_delta = 0; // "Generic" here includes megamorphic.
404 ComputeTypeInfoCountDelta(old_state, new_state, &polymorphic_delta,
405 &generic_delta);
406 TypeFeedbackInfo* info = TypeFeedbackInfo::cast(host->type_feedback_info());
407 info->change_ic_with_type_info_count(polymorphic_delta);
408 info->change_ic_generic_count(generic_delta);
409 }
410 if (host->type_feedback_info()->IsTypeFeedbackInfo()) {
411 TypeFeedbackInfo* info = TypeFeedbackInfo::cast(host->type_feedback_info());
412 info->change_own_type_change_checksum();
413 }
414 host->set_profiler_ticks(0);
415 isolate->runtime_profiler()->NotifyICChanged();
416 // TODO(2029): When an optimized function is patched, it would
417 // be nice to propagate the corresponding type information to its
418 // unoptimized version for the benefit of later inlining.
419 }
420
421
PostPatching(Address address,Code * target,Code * old_target)422 void IC::PostPatching(Address address, Code* target, Code* old_target) {
423 // Type vector based ICs update these statistics at a different time because
424 // they don't always patch on state change.
425 if (target->kind() == Code::CALL_IC) return;
426
427 Isolate* isolate = target->GetHeap()->isolate();
428 State old_state = UNINITIALIZED;
429 State new_state = UNINITIALIZED;
430 bool target_remains_ic_stub = false;
431 if (old_target->is_inline_cache_stub() && target->is_inline_cache_stub()) {
432 old_state = old_target->ic_state();
433 new_state = target->ic_state();
434 target_remains_ic_stub = true;
435 }
436
437 OnTypeFeedbackChanged(isolate, address, old_state, new_state,
438 target_remains_ic_stub);
439 }
440
441
RegisterWeakMapDependency(Handle<Code> stub)442 void IC::RegisterWeakMapDependency(Handle<Code> stub) {
443 if (FLAG_collect_maps && FLAG_weak_embedded_maps_in_ic &&
444 stub->CanBeWeakStub()) {
445 DCHECK(!stub->is_weak_stub());
446 MapHandleList maps;
447 stub->FindAllMaps(&maps);
448 if (maps.length() == 1 && stub->IsWeakObjectInIC(*maps.at(0))) {
449 Map::AddDependentIC(maps.at(0), stub);
450 stub->mark_as_weak_stub();
451 if (FLAG_enable_ool_constant_pool) {
452 stub->constant_pool()->set_weak_object_state(
453 ConstantPoolArray::WEAK_OBJECTS_IN_IC);
454 }
455 }
456 }
457 }
458
459
InvalidateMaps(Code * stub)460 void IC::InvalidateMaps(Code* stub) {
461 DCHECK(stub->is_weak_stub());
462 stub->mark_as_invalidated_weak_stub();
463 Isolate* isolate = stub->GetIsolate();
464 Heap* heap = isolate->heap();
465 Object* undefined = heap->undefined_value();
466 int mode_mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
467 for (RelocIterator it(stub, mode_mask); !it.done(); it.next()) {
468 RelocInfo::Mode mode = it.rinfo()->rmode();
469 if (mode == RelocInfo::EMBEDDED_OBJECT &&
470 it.rinfo()->target_object()->IsMap()) {
471 it.rinfo()->set_target_object(undefined, SKIP_WRITE_BARRIER);
472 }
473 }
474 CpuFeatures::FlushICache(stub->instruction_start(), stub->instruction_size());
475 }
476
477
Clear(Isolate * isolate,Address address,ConstantPoolArray * constant_pool)478 void IC::Clear(Isolate* isolate, Address address,
479 ConstantPoolArray* constant_pool) {
480 Code* target = GetTargetAtAddress(address, constant_pool);
481
482 // Don't clear debug break inline cache as it will remove the break point.
483 if (target->is_debug_stub()) return;
484
485 switch (target->kind()) {
486 case Code::LOAD_IC:
487 return LoadIC::Clear(isolate, address, target, constant_pool);
488 case Code::KEYED_LOAD_IC:
489 return KeyedLoadIC::Clear(isolate, address, target, constant_pool);
490 case Code::STORE_IC:
491 return StoreIC::Clear(isolate, address, target, constant_pool);
492 case Code::KEYED_STORE_IC:
493 return KeyedStoreIC::Clear(isolate, address, target, constant_pool);
494 case Code::CALL_IC:
495 return CallIC::Clear(isolate, address, target, constant_pool);
496 case Code::COMPARE_IC:
497 return CompareIC::Clear(isolate, address, target, constant_pool);
498 case Code::COMPARE_NIL_IC:
499 return CompareNilIC::Clear(address, target, constant_pool);
500 case Code::BINARY_OP_IC:
501 case Code::TO_BOOLEAN_IC:
502 // Clearing these is tricky and does not
503 // make any performance difference.
504 return;
505 default:
506 UNREACHABLE();
507 }
508 }
509
510
Clear(Isolate * isolate,Address address,Code * target,ConstantPoolArray * constant_pool)511 void KeyedLoadIC::Clear(Isolate* isolate, Address address, Code* target,
512 ConstantPoolArray* constant_pool) {
513 if (IsCleared(target)) return;
514 // Make sure to also clear the map used in inline fast cases. If we
515 // do not clear these maps, cached code can keep objects alive
516 // through the embedded maps.
517 SetTargetAtAddress(address, *pre_monomorphic_stub(isolate), constant_pool);
518 }
519
520
Clear(Isolate * isolate,Address address,Code * target,ConstantPoolArray * constant_pool)521 void CallIC::Clear(Isolate* isolate, Address address, Code* target,
522 ConstantPoolArray* constant_pool) {
523 // Currently, CallIC doesn't have state changes.
524 }
525
526
Clear(Isolate * isolate,Address address,Code * target,ConstantPoolArray * constant_pool)527 void LoadIC::Clear(Isolate* isolate, Address address, Code* target,
528 ConstantPoolArray* constant_pool) {
529 if (IsCleared(target)) return;
530 Code* code = PropertyICCompiler::FindPreMonomorphic(isolate, Code::LOAD_IC,
531 target->extra_ic_state());
532 SetTargetAtAddress(address, code, constant_pool);
533 }
534
535
Clear(Isolate * isolate,Address address,Code * target,ConstantPoolArray * constant_pool)536 void StoreIC::Clear(Isolate* isolate, Address address, Code* target,
537 ConstantPoolArray* constant_pool) {
538 if (IsCleared(target)) return;
539 Code* code = PropertyICCompiler::FindPreMonomorphic(isolate, Code::STORE_IC,
540 target->extra_ic_state());
541 SetTargetAtAddress(address, code, constant_pool);
542 }
543
544
Clear(Isolate * isolate,Address address,Code * target,ConstantPoolArray * constant_pool)545 void KeyedStoreIC::Clear(Isolate* isolate, Address address, Code* target,
546 ConstantPoolArray* constant_pool) {
547 if (IsCleared(target)) return;
548 SetTargetAtAddress(
549 address, *pre_monomorphic_stub(
550 isolate, StoreIC::GetStrictMode(target->extra_ic_state())),
551 constant_pool);
552 }
553
554
Clear(Isolate * isolate,Address address,Code * target,ConstantPoolArray * constant_pool)555 void CompareIC::Clear(Isolate* isolate, Address address, Code* target,
556 ConstantPoolArray* constant_pool) {
557 DCHECK(CodeStub::GetMajorKey(target) == CodeStub::CompareIC);
558 CompareICStub stub(target->stub_key(), isolate);
559 // Only clear CompareICs that can retain objects.
560 if (stub.state() != CompareICState::KNOWN_OBJECT) return;
561 SetTargetAtAddress(address, GetRawUninitialized(isolate, stub.op()),
562 constant_pool);
563 PatchInlinedSmiCode(address, DISABLE_INLINED_SMI_CHECK);
564 }
565
566
567 // static
generic_stub(Isolate * isolate)568 Handle<Code> KeyedLoadIC::generic_stub(Isolate* isolate) {
569 if (FLAG_compiled_keyed_generic_loads) {
570 return KeyedLoadGenericStub(isolate).GetCode();
571 } else {
572 return isolate->builtins()->KeyedLoadIC_Generic();
573 }
574 }
575
576
MigrateDeprecated(Handle<Object> object)577 static bool MigrateDeprecated(Handle<Object> object) {
578 if (!object->IsJSObject()) return false;
579 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
580 if (!receiver->map()->is_deprecated()) return false;
581 JSObject::MigrateInstance(Handle<JSObject>::cast(object));
582 return true;
583 }
584
585
Load(Handle<Object> object,Handle<Name> name)586 MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
587 // If the object is undefined or null it's illegal to try to get any
588 // of its properties; throw a TypeError in that case.
589 if (object->IsUndefined() || object->IsNull()) {
590 return TypeError("non_object_property_load", object, name);
591 }
592
593 // Check if the name is trivially convertible to an index and get
594 // the element or char if so.
595 uint32_t index;
596 if (kind() == Code::KEYED_LOAD_IC && name->AsArrayIndex(&index)) {
597 // Rewrite to the generic keyed load stub.
598 if (FLAG_use_ic) {
599 set_target(*KeyedLoadIC::generic_stub(isolate()));
600 TRACE_IC("LoadIC", name);
601 TRACE_GENERIC_IC(isolate(), "LoadIC", "name as array index");
602 }
603 Handle<Object> result;
604 ASSIGN_RETURN_ON_EXCEPTION(
605 isolate(), result,
606 Runtime::GetElementOrCharAt(isolate(), object, index), Object);
607 return result;
608 }
609
610 bool use_ic = MigrateDeprecated(object) ? false : FLAG_use_ic;
611
612 // Named lookup in the object.
613 LookupIterator it(object, name);
614 LookupForRead(&it);
615
616 if (it.IsFound() || !IsUndeclaredGlobal(object)) {
617 // Update inline cache and stub cache.
618 if (use_ic) UpdateCaches(&it);
619
620 // Get the property.
621 Handle<Object> result;
622 ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, Object::GetProperty(&it),
623 Object);
624 if (it.IsFound()) {
625 return result;
626 } else if (!IsUndeclaredGlobal(object)) {
627 LOG(isolate(), SuspectReadEvent(*name, *object));
628 return result;
629 }
630 }
631 return ReferenceError("not_defined", name);
632 }
633
634
AddOneReceiverMapIfMissing(MapHandleList * receiver_maps,Handle<Map> new_receiver_map)635 static bool AddOneReceiverMapIfMissing(MapHandleList* receiver_maps,
636 Handle<Map> new_receiver_map) {
637 DCHECK(!new_receiver_map.is_null());
638 for (int current = 0; current < receiver_maps->length(); ++current) {
639 if (!receiver_maps->at(current).is_null() &&
640 receiver_maps->at(current).is_identical_to(new_receiver_map)) {
641 return false;
642 }
643 }
644 receiver_maps->Add(new_receiver_map);
645 return true;
646 }
647
648
UpdatePolymorphicIC(Handle<Name> name,Handle<Code> code)649 bool IC::UpdatePolymorphicIC(Handle<Name> name, Handle<Code> code) {
650 if (!code->is_handler()) return false;
651 if (target()->is_keyed_stub() && state() != PROTOTYPE_FAILURE) return false;
652 Handle<HeapType> type = receiver_type();
653 TypeHandleList types;
654 CodeHandleList handlers;
655
656 TargetTypes(&types);
657 int number_of_types = types.length();
658 int deprecated_types = 0;
659 int handler_to_overwrite = -1;
660
661 for (int i = 0; i < number_of_types; i++) {
662 Handle<HeapType> current_type = types.at(i);
663 if (current_type->IsClass() &&
664 current_type->AsClass()->Map()->is_deprecated()) {
665 // Filter out deprecated maps to ensure their instances get migrated.
666 ++deprecated_types;
667 } else if (type->NowIs(current_type)) {
668 // If the receiver type is already in the polymorphic IC, this indicates
669 // there was a prototoype chain failure. In that case, just overwrite the
670 // handler.
671 handler_to_overwrite = i;
672 } else if (handler_to_overwrite == -1 && current_type->IsClass() &&
673 type->IsClass() &&
674 IsTransitionOfMonomorphicTarget(*current_type->AsClass()->Map(),
675 *type->AsClass()->Map())) {
676 handler_to_overwrite = i;
677 }
678 }
679
680 int number_of_valid_types =
681 number_of_types - deprecated_types - (handler_to_overwrite != -1);
682
683 if (number_of_valid_types >= 4) return false;
684 if (number_of_types == 0) return false;
685 if (!target()->FindHandlers(&handlers, types.length())) return false;
686
687 number_of_valid_types++;
688 if (number_of_valid_types > 1 && target()->is_keyed_stub()) return false;
689 Handle<Code> ic;
690 if (number_of_valid_types == 1) {
691 ic = PropertyICCompiler::ComputeMonomorphic(kind(), name, type, code,
692 extra_ic_state());
693 } else {
694 if (handler_to_overwrite >= 0) {
695 handlers.Set(handler_to_overwrite, code);
696 if (!type->NowIs(types.at(handler_to_overwrite))) {
697 types.Set(handler_to_overwrite, type);
698 }
699 } else {
700 types.Add(type);
701 handlers.Add(code);
702 }
703 ic = PropertyICCompiler::ComputePolymorphic(kind(), &types, &handlers,
704 number_of_valid_types, name,
705 extra_ic_state());
706 }
707 set_target(*ic);
708 return true;
709 }
710
711
CurrentTypeOf(Handle<Object> object,Isolate * isolate)712 Handle<HeapType> IC::CurrentTypeOf(Handle<Object> object, Isolate* isolate) {
713 return object->IsJSGlobalObject()
714 ? HeapType::Constant(Handle<JSGlobalObject>::cast(object), isolate)
715 : HeapType::NowOf(object, isolate);
716 }
717
718
TypeToMap(HeapType * type,Isolate * isolate)719 Handle<Map> IC::TypeToMap(HeapType* type, Isolate* isolate) {
720 if (type->Is(HeapType::Number()))
721 return isolate->factory()->heap_number_map();
722 if (type->Is(HeapType::Boolean())) return isolate->factory()->boolean_map();
723 if (type->IsConstant()) {
724 return handle(
725 Handle<JSGlobalObject>::cast(type->AsConstant()->Value())->map());
726 }
727 DCHECK(type->IsClass());
728 return type->AsClass()->Map();
729 }
730
731
732 template <class T>
MapToType(Handle<Map> map,typename T::Region * region)733 typename T::TypeHandle IC::MapToType(Handle<Map> map,
734 typename T::Region* region) {
735 if (map->instance_type() == HEAP_NUMBER_TYPE) {
736 return T::Number(region);
737 } else if (map->instance_type() == ODDBALL_TYPE) {
738 // The only oddballs that can be recorded in ICs are booleans.
739 return T::Boolean(region);
740 } else {
741 return T::Class(map, region);
742 }
743 }
744
745
746 template Type* IC::MapToType<Type>(Handle<Map> map, Zone* zone);
747
748
749 template Handle<HeapType> IC::MapToType<HeapType>(Handle<Map> map,
750 Isolate* region);
751
752
UpdateMonomorphicIC(Handle<Code> handler,Handle<Name> name)753 void IC::UpdateMonomorphicIC(Handle<Code> handler, Handle<Name> name) {
754 DCHECK(handler->is_handler());
755 Handle<Code> ic = PropertyICCompiler::ComputeMonomorphic(
756 kind(), name, receiver_type(), handler, extra_ic_state());
757 set_target(*ic);
758 }
759
760
CopyICToMegamorphicCache(Handle<Name> name)761 void IC::CopyICToMegamorphicCache(Handle<Name> name) {
762 TypeHandleList types;
763 CodeHandleList handlers;
764 TargetTypes(&types);
765 if (!target()->FindHandlers(&handlers, types.length())) return;
766 for (int i = 0; i < types.length(); i++) {
767 UpdateMegamorphicCache(*types.at(i), *name, *handlers.at(i));
768 }
769 }
770
771
IsTransitionOfMonomorphicTarget(Map * source_map,Map * target_map)772 bool IC::IsTransitionOfMonomorphicTarget(Map* source_map, Map* target_map) {
773 if (source_map == NULL) return true;
774 if (target_map == NULL) return false;
775 ElementsKind target_elements_kind = target_map->elements_kind();
776 bool more_general_transition = IsMoreGeneralElementsKindTransition(
777 source_map->elements_kind(), target_elements_kind);
778 Map* transitioned_map =
779 more_general_transition
780 ? source_map->LookupElementsTransitionMap(target_elements_kind)
781 : NULL;
782
783 return transitioned_map == target_map;
784 }
785
786
PatchCache(Handle<Name> name,Handle<Code> code)787 void IC::PatchCache(Handle<Name> name, Handle<Code> code) {
788 switch (state()) {
789 case UNINITIALIZED:
790 case PREMONOMORPHIC:
791 UpdateMonomorphicIC(code, name);
792 break;
793 case PROTOTYPE_FAILURE:
794 case MONOMORPHIC:
795 case POLYMORPHIC:
796 if (!target()->is_keyed_stub() || state() == PROTOTYPE_FAILURE) {
797 if (UpdatePolymorphicIC(name, code)) break;
798 CopyICToMegamorphicCache(name);
799 }
800 set_target(*megamorphic_stub());
801 // Fall through.
802 case MEGAMORPHIC:
803 UpdateMegamorphicCache(*receiver_type(), *name, *code);
804 break;
805 case DEBUG_STUB:
806 break;
807 case DEFAULT:
808 case GENERIC:
809 UNREACHABLE();
810 break;
811 }
812 }
813
814
initialize_stub(Isolate * isolate,ExtraICState extra_state)815 Handle<Code> LoadIC::initialize_stub(Isolate* isolate,
816 ExtraICState extra_state) {
817 return PropertyICCompiler::ComputeLoad(isolate, UNINITIALIZED, extra_state);
818 }
819
820
megamorphic_stub()821 Handle<Code> LoadIC::megamorphic_stub() {
822 if (kind() == Code::LOAD_IC) {
823 MegamorphicLoadStub stub(isolate(), LoadICState(extra_ic_state()));
824 return stub.GetCode();
825 } else {
826 DCHECK_EQ(Code::KEYED_LOAD_IC, kind());
827 return KeyedLoadIC::generic_stub(isolate());
828 }
829 }
830
831
pre_monomorphic_stub(Isolate * isolate,ExtraICState extra_state)832 Handle<Code> LoadIC::pre_monomorphic_stub(Isolate* isolate,
833 ExtraICState extra_state) {
834 return PropertyICCompiler::ComputeLoad(isolate, PREMONOMORPHIC, extra_state);
835 }
836
837
pre_monomorphic_stub(Isolate * isolate)838 Handle<Code> KeyedLoadIC::pre_monomorphic_stub(Isolate* isolate) {
839 return isolate->builtins()->KeyedLoadIC_PreMonomorphic();
840 }
841
842
pre_monomorphic_stub() const843 Handle<Code> LoadIC::pre_monomorphic_stub() const {
844 if (kind() == Code::LOAD_IC) {
845 return LoadIC::pre_monomorphic_stub(isolate(), extra_ic_state());
846 } else {
847 DCHECK_EQ(Code::KEYED_LOAD_IC, kind());
848 return KeyedLoadIC::pre_monomorphic_stub(isolate());
849 }
850 }
851
852
SimpleFieldLoad(FieldIndex index)853 Handle<Code> LoadIC::SimpleFieldLoad(FieldIndex index) {
854 LoadFieldStub stub(isolate(), index);
855 return stub.GetCode();
856 }
857
858
UpdateCaches(LookupIterator * lookup)859 void LoadIC::UpdateCaches(LookupIterator* lookup) {
860 if (state() == UNINITIALIZED) {
861 // This is the first time we execute this inline cache. Set the target to
862 // the pre monomorphic stub to delay setting the monomorphic state.
863 set_target(*pre_monomorphic_stub());
864 TRACE_IC("LoadIC", lookup->name());
865 return;
866 }
867
868 Handle<Code> code;
869 if (lookup->state() == LookupIterator::JSPROXY ||
870 lookup->state() == LookupIterator::ACCESS_CHECK) {
871 code = slow_stub();
872 } else if (!lookup->IsFound()) {
873 if (kind() == Code::LOAD_IC) {
874 code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(lookup->name(),
875 receiver_type());
876 // TODO(jkummerow/verwaest): Introduce a builtin that handles this case.
877 if (code.is_null()) code = slow_stub();
878 } else {
879 code = slow_stub();
880 }
881 } else {
882 code = ComputeHandler(lookup);
883 }
884
885 PatchCache(lookup->name(), code);
886 TRACE_IC("LoadIC", lookup->name());
887 }
888
889
UpdateMegamorphicCache(HeapType * type,Name * name,Code * code)890 void IC::UpdateMegamorphicCache(HeapType* type, Name* name, Code* code) {
891 if (kind() == Code::KEYED_LOAD_IC || kind() == Code::KEYED_STORE_IC) return;
892 Map* map = *TypeToMap(type, isolate());
893 isolate()->stub_cache()->Set(name, map, code);
894 }
895
896
ComputeHandler(LookupIterator * lookup,Handle<Object> value)897 Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> value) {
898 bool receiver_is_holder =
899 lookup->GetReceiver().is_identical_to(lookup->GetHolder<JSObject>());
900 CacheHolderFlag flag;
901 Handle<Map> stub_holder_map = IC::GetHandlerCacheHolder(
902 *receiver_type(), receiver_is_holder, isolate(), &flag);
903
904 Handle<Code> code = PropertyHandlerCompiler::Find(
905 lookup->name(), stub_holder_map, kind(), flag,
906 lookup->is_dictionary_holder() ? Code::NORMAL : Code::FAST);
907 // Use the cached value if it exists, and if it is different from the
908 // handler that just missed.
909 if (!code.is_null()) {
910 if (!maybe_handler_.is_null() &&
911 !maybe_handler_.ToHandleChecked().is_identical_to(code)) {
912 return code;
913 }
914 if (maybe_handler_.is_null()) {
915 // maybe_handler_ is only populated for MONOMORPHIC and POLYMORPHIC ICs.
916 // In MEGAMORPHIC case, check if the handler in the megamorphic stub
917 // cache (which just missed) is different from the cached handler.
918 if (state() == MEGAMORPHIC && lookup->GetReceiver()->IsHeapObject()) {
919 Map* map = Handle<HeapObject>::cast(lookup->GetReceiver())->map();
920 Code* megamorphic_cached_code =
921 isolate()->stub_cache()->Get(*lookup->name(), map, code->flags());
922 if (megamorphic_cached_code != *code) return code;
923 } else {
924 return code;
925 }
926 }
927 }
928
929 code = CompileHandler(lookup, value, flag);
930 DCHECK(code->is_handler());
931
932 // TODO(mvstanton): we'd only like to cache code on the map when it's custom
933 // code compiled for this map, otherwise it's already cached in the global
934 // code
935 // cache. We are also guarding against installing code with flags that don't
936 // match the desired CacheHolderFlag computed above, which would lead to
937 // invalid lookups later.
938 if (code->type() != Code::NORMAL &&
939 Code::ExtractCacheHolderFromFlags(code->flags()) == flag) {
940 Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
941 }
942
943 return code;
944 }
945
946
CompileHandler(LookupIterator * lookup,Handle<Object> unused,CacheHolderFlag cache_holder)947 Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
948 Handle<Object> unused,
949 CacheHolderFlag cache_holder) {
950 Handle<Object> receiver = lookup->GetReceiver();
951 if (receiver->IsString() &&
952 Name::Equals(isolate()->factory()->length_string(), lookup->name())) {
953 FieldIndex index = FieldIndex::ForInObjectOffset(String::kLengthOffset);
954 return SimpleFieldLoad(index);
955 }
956
957 if (receiver->IsStringWrapper() &&
958 Name::Equals(isolate()->factory()->length_string(), lookup->name())) {
959 StringLengthStub string_length_stub(isolate());
960 return string_length_stub.GetCode();
961 }
962
963 // Use specialized code for getting prototype of functions.
964 if (receiver->IsJSFunction() &&
965 Name::Equals(isolate()->factory()->prototype_string(), lookup->name()) &&
966 Handle<JSFunction>::cast(receiver)->should_have_prototype() &&
967 !Handle<JSFunction>::cast(receiver)
968 ->map()
969 ->has_non_instance_prototype()) {
970 Handle<Code> stub;
971 FunctionPrototypeStub function_prototype_stub(isolate());
972 return function_prototype_stub.GetCode();
973 }
974
975 Handle<HeapType> type = receiver_type();
976 Handle<JSObject> holder = lookup->GetHolder<JSObject>();
977 bool receiver_is_holder = receiver.is_identical_to(holder);
978 switch (lookup->state()) {
979 case LookupIterator::INTERCEPTOR: {
980 DCHECK(!holder->GetNamedInterceptor()->getter()->IsUndefined());
981 NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
982 cache_holder);
983 // Perform a lookup behind the interceptor. Copy the LookupIterator since
984 // the original iterator will be used to fetch the value.
985 LookupIterator it = *lookup;
986 it.Next();
987 LookupForRead(&it);
988 return compiler.CompileLoadInterceptor(&it);
989 }
990
991 case LookupIterator::ACCESSOR: {
992 // Use simple field loads for some well-known callback properties.
993 if (receiver_is_holder) {
994 DCHECK(receiver->IsJSObject());
995 Handle<JSObject> js_receiver = Handle<JSObject>::cast(receiver);
996 int object_offset;
997 if (Accessors::IsJSObjectFieldAccessor<HeapType>(type, lookup->name(),
998 &object_offset)) {
999 FieldIndex index =
1000 FieldIndex::ForInObjectOffset(object_offset, js_receiver->map());
1001 return SimpleFieldLoad(index);
1002 }
1003 }
1004
1005 Handle<Object> accessors = lookup->GetAccessors();
1006 if (accessors->IsExecutableAccessorInfo()) {
1007 Handle<ExecutableAccessorInfo> info =
1008 Handle<ExecutableAccessorInfo>::cast(accessors);
1009 if (v8::ToCData<Address>(info->getter()) == 0) break;
1010 if (!ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), info,
1011 type)) {
1012 break;
1013 }
1014 if (!holder->HasFastProperties()) break;
1015 NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
1016 cache_holder);
1017 return compiler.CompileLoadCallback(lookup->name(), info);
1018 }
1019 if (accessors->IsAccessorPair()) {
1020 Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
1021 isolate());
1022 if (!getter->IsJSFunction()) break;
1023 if (!holder->HasFastProperties()) break;
1024 Handle<JSFunction> function = Handle<JSFunction>::cast(getter);
1025 if (!receiver->IsJSObject() && !function->IsBuiltin() &&
1026 function->shared()->strict_mode() == SLOPPY) {
1027 // Calling sloppy non-builtins with a value as the receiver
1028 // requires boxing.
1029 break;
1030 }
1031 CallOptimization call_optimization(function);
1032 NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
1033 cache_holder);
1034 if (call_optimization.is_simple_api_call() &&
1035 call_optimization.IsCompatibleReceiver(receiver, holder)) {
1036 return compiler.CompileLoadCallback(lookup->name(),
1037 call_optimization);
1038 }
1039 return compiler.CompileLoadViaGetter(lookup->name(), function);
1040 }
1041 // TODO(dcarney): Handle correctly.
1042 DCHECK(accessors->IsDeclaredAccessorInfo());
1043 break;
1044 }
1045
1046 case LookupIterator::DATA: {
1047 if (lookup->is_dictionary_holder()) {
1048 if (kind() != Code::LOAD_IC) break;
1049 if (holder->IsGlobalObject()) {
1050 NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
1051 cache_holder);
1052 Handle<PropertyCell> cell = lookup->GetPropertyCell();
1053 Handle<Code> code = compiler.CompileLoadGlobal(
1054 cell, lookup->name(), lookup->IsConfigurable());
1055 // TODO(verwaest): Move caching of these NORMAL stubs outside as well.
1056 CacheHolderFlag flag;
1057 Handle<Map> stub_holder_map = GetHandlerCacheHolder(
1058 *type, receiver_is_holder, isolate(), &flag);
1059 Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
1060 return code;
1061 }
1062 // There is only one shared stub for loading normalized
1063 // properties. It does not traverse the prototype chain, so the
1064 // property must be found in the object for the stub to be
1065 // applicable.
1066 if (!receiver_is_holder) break;
1067 return isolate()->builtins()->LoadIC_Normal();
1068 }
1069
1070 // -------------- Fields --------------
1071 if (lookup->property_details().type() == FIELD) {
1072 FieldIndex field = lookup->GetFieldIndex();
1073 if (receiver_is_holder) {
1074 return SimpleFieldLoad(field);
1075 }
1076 NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
1077 cache_holder);
1078 return compiler.CompileLoadField(lookup->name(), field);
1079 }
1080
1081 // -------------- Constant properties --------------
1082 DCHECK(lookup->property_details().type() == CONSTANT);
1083 if (receiver_is_holder) {
1084 LoadConstantStub stub(isolate(), lookup->GetConstantIndex());
1085 return stub.GetCode();
1086 }
1087 NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
1088 cache_holder);
1089 return compiler.CompileLoadConstant(lookup->name(),
1090 lookup->GetConstantIndex());
1091 }
1092
1093 case LookupIterator::ACCESS_CHECK:
1094 case LookupIterator::JSPROXY:
1095 case LookupIterator::NOT_FOUND:
1096 case LookupIterator::TRANSITION:
1097 UNREACHABLE();
1098 }
1099
1100 return slow_stub();
1101 }
1102
1103
TryConvertKey(Handle<Object> key,Isolate * isolate)1104 static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
1105 // This helper implements a few common fast cases for converting
1106 // non-smi keys of keyed loads/stores to a smi or a string.
1107 if (key->IsHeapNumber()) {
1108 double value = Handle<HeapNumber>::cast(key)->value();
1109 if (std::isnan(value)) {
1110 key = isolate->factory()->nan_string();
1111 } else {
1112 int int_value = FastD2I(value);
1113 if (value == int_value && Smi::IsValid(int_value)) {
1114 key = Handle<Smi>(Smi::FromInt(int_value), isolate);
1115 }
1116 }
1117 } else if (key->IsUndefined()) {
1118 key = isolate->factory()->undefined_string();
1119 }
1120 return key;
1121 }
1122
1123
LoadElementStub(Handle<JSObject> receiver)1124 Handle<Code> KeyedLoadIC::LoadElementStub(Handle<JSObject> receiver) {
1125 Handle<Map> receiver_map(receiver->map(), isolate());
1126 MapHandleList target_receiver_maps;
1127 if (target().is_identical_to(string_stub())) {
1128 target_receiver_maps.Add(isolate()->factory()->string_map());
1129 } else {
1130 TargetMaps(&target_receiver_maps);
1131 }
1132 if (target_receiver_maps.length() == 0) {
1133 return PropertyICCompiler::ComputeKeyedLoadMonomorphic(receiver_map);
1134 }
1135
1136 // The first time a receiver is seen that is a transitioned version of the
1137 // previous monomorphic receiver type, assume the new ElementsKind is the
1138 // monomorphic type. This benefits global arrays that only transition
1139 // once, and all call sites accessing them are faster if they remain
1140 // monomorphic. If this optimistic assumption is not true, the IC will
1141 // miss again and it will become polymorphic and support both the
1142 // untransitioned and transitioned maps.
1143 if (state() == MONOMORPHIC && IsMoreGeneralElementsKindTransition(
1144 target_receiver_maps.at(0)->elements_kind(),
1145 receiver->GetElementsKind())) {
1146 return PropertyICCompiler::ComputeKeyedLoadMonomorphic(receiver_map);
1147 }
1148
1149 DCHECK(state() != GENERIC);
1150
1151 // Determine the list of receiver maps that this call site has seen,
1152 // adding the map that was just encountered.
1153 if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map)) {
1154 // If the miss wasn't due to an unseen map, a polymorphic stub
1155 // won't help, use the generic stub.
1156 TRACE_GENERIC_IC(isolate(), "KeyedLoadIC", "same map added twice");
1157 return generic_stub();
1158 }
1159
1160 // If the maximum number of receiver maps has been exceeded, use the generic
1161 // version of the IC.
1162 if (target_receiver_maps.length() > kMaxKeyedPolymorphism) {
1163 TRACE_GENERIC_IC(isolate(), "KeyedLoadIC", "max polymorph exceeded");
1164 return generic_stub();
1165 }
1166
1167 return PropertyICCompiler::ComputeKeyedLoadPolymorphic(&target_receiver_maps);
1168 }
1169
1170
Load(Handle<Object> object,Handle<Object> key)1171 MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
1172 Handle<Object> key) {
1173 if (MigrateDeprecated(object)) {
1174 Handle<Object> result;
1175 ASSIGN_RETURN_ON_EXCEPTION(
1176 isolate(), result, Runtime::GetObjectProperty(isolate(), object, key),
1177 Object);
1178 return result;
1179 }
1180
1181 Handle<Object> load_handle;
1182 Handle<Code> stub = generic_stub();
1183
1184 // Check for non-string values that can be converted into an
1185 // internalized string directly or is representable as a smi.
1186 key = TryConvertKey(key, isolate());
1187
1188 if (key->IsInternalizedString() || key->IsSymbol()) {
1189 ASSIGN_RETURN_ON_EXCEPTION(isolate(), load_handle,
1190 LoadIC::Load(object, Handle<Name>::cast(key)),
1191 Object);
1192 } else if (FLAG_use_ic && !object->IsAccessCheckNeeded()) {
1193 if (object->IsString() && key->IsNumber()) {
1194 if (state() == UNINITIALIZED) stub = string_stub();
1195 } else if (object->IsJSObject()) {
1196 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1197 if (!Object::ToSmi(isolate(), key).is_null()) {
1198 stub = LoadElementStub(receiver);
1199 }
1200 }
1201 }
1202
1203 if (!is_target_set()) {
1204 Code* generic = *generic_stub();
1205 if (*stub == generic) {
1206 TRACE_GENERIC_IC(isolate(), "KeyedLoadIC", "set generic");
1207 }
1208 set_target(*stub);
1209 TRACE_IC("LoadIC", key);
1210 }
1211
1212 if (!load_handle.is_null()) return load_handle;
1213 Handle<Object> result;
1214 ASSIGN_RETURN_ON_EXCEPTION(isolate(), result,
1215 Runtime::GetObjectProperty(isolate(), object, key),
1216 Object);
1217 return result;
1218 }
1219
1220
LookupForWrite(LookupIterator * it,Handle<Object> value,JSReceiver::StoreFromKeyed store_mode)1221 bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
1222 JSReceiver::StoreFromKeyed store_mode) {
1223 // Disable ICs for non-JSObjects for now.
1224 Handle<Object> receiver = it->GetReceiver();
1225 if (!receiver->IsJSObject()) return false;
1226 DCHECK(!Handle<JSObject>::cast(receiver)->map()->is_deprecated());
1227
1228 for (; it->IsFound(); it->Next()) {
1229 switch (it->state()) {
1230 case LookupIterator::NOT_FOUND:
1231 case LookupIterator::TRANSITION:
1232 UNREACHABLE();
1233 case LookupIterator::JSPROXY:
1234 return false;
1235 case LookupIterator::INTERCEPTOR: {
1236 Handle<JSObject> holder = it->GetHolder<JSObject>();
1237 InterceptorInfo* info = holder->GetNamedInterceptor();
1238 if (it->HolderIsReceiverOrHiddenPrototype()) {
1239 if (!info->setter()->IsUndefined()) return true;
1240 } else if (!info->getter()->IsUndefined() ||
1241 !info->query()->IsUndefined()) {
1242 return false;
1243 }
1244 break;
1245 }
1246 case LookupIterator::ACCESS_CHECK:
1247 if (it->GetHolder<JSObject>()->IsAccessCheckNeeded()) return false;
1248 break;
1249 case LookupIterator::ACCESSOR:
1250 return !it->IsReadOnly();
1251 case LookupIterator::DATA: {
1252 if (it->IsReadOnly()) return false;
1253 Handle<JSObject> holder = it->GetHolder<JSObject>();
1254 if (receiver.is_identical_to(holder)) {
1255 it->PrepareForDataProperty(value);
1256 // The previous receiver map might just have been deprecated,
1257 // so reload it.
1258 update_receiver_type(receiver);
1259 return true;
1260 }
1261
1262 // Receiver != holder.
1263 PrototypeIterator iter(it->isolate(), receiver);
1264 if (receiver->IsJSGlobalProxy()) {
1265 return it->GetHolder<Object>().is_identical_to(
1266 PrototypeIterator::GetCurrent(iter));
1267 }
1268
1269 it->PrepareTransitionToDataProperty(value, NONE, store_mode);
1270 return it->IsCacheableTransition();
1271 }
1272 }
1273 }
1274
1275 it->PrepareTransitionToDataProperty(value, NONE, store_mode);
1276 return it->IsCacheableTransition();
1277 }
1278
1279
Store(Handle<Object> object,Handle<Name> name,Handle<Object> value,JSReceiver::StoreFromKeyed store_mode)1280 MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
1281 Handle<Object> value,
1282 JSReceiver::StoreFromKeyed store_mode) {
1283 // TODO(verwaest): Let SetProperty do the migration, since storing a property
1284 // might deprecate the current map again, if value does not fit.
1285 if (MigrateDeprecated(object) || object->IsJSProxy()) {
1286 Handle<Object> result;
1287 ASSIGN_RETURN_ON_EXCEPTION(
1288 isolate(), result,
1289 Object::SetProperty(object, name, value, strict_mode()), Object);
1290 return result;
1291 }
1292
1293 // If the object is undefined or null it's illegal to try to set any
1294 // properties on it; throw a TypeError in that case.
1295 if (object->IsUndefined() || object->IsNull()) {
1296 return TypeError("non_object_property_store", object, name);
1297 }
1298
1299 // Check if the given name is an array index.
1300 uint32_t index;
1301 if (name->AsArrayIndex(&index)) {
1302 // Ignore other stores where the receiver is not a JSObject.
1303 // TODO(1475): Must check prototype chains of object wrappers.
1304 if (!object->IsJSObject()) return value;
1305 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1306
1307 Handle<Object> result;
1308 ASSIGN_RETURN_ON_EXCEPTION(
1309 isolate(), result,
1310 JSObject::SetElement(receiver, index, value, NONE, strict_mode()),
1311 Object);
1312 return value;
1313 }
1314
1315 // Observed objects are always modified through the runtime.
1316 if (object->IsHeapObject() &&
1317 Handle<HeapObject>::cast(object)->map()->is_observed()) {
1318 Handle<Object> result;
1319 ASSIGN_RETURN_ON_EXCEPTION(
1320 isolate(), result,
1321 Object::SetProperty(object, name, value, strict_mode(), store_mode),
1322 Object);
1323 return result;
1324 }
1325
1326 LookupIterator it(object, name);
1327 if (FLAG_use_ic) UpdateCaches(&it, value, store_mode);
1328
1329 // Set the property.
1330 Handle<Object> result;
1331 ASSIGN_RETURN_ON_EXCEPTION(
1332 isolate(), result,
1333 Object::SetProperty(&it, value, strict_mode(), store_mode), Object);
1334 return result;
1335 }
1336
1337
initialize_stub(Isolate * isolate,int argc,CallICState::CallType call_type)1338 Handle<Code> CallIC::initialize_stub(Isolate* isolate, int argc,
1339 CallICState::CallType call_type) {
1340 CallICStub stub(isolate, CallICState(argc, call_type));
1341 Handle<Code> code = stub.GetCode();
1342 return code;
1343 }
1344
1345
initialize_stub(Isolate * isolate,StrictMode strict_mode)1346 Handle<Code> StoreIC::initialize_stub(Isolate* isolate,
1347 StrictMode strict_mode) {
1348 ExtraICState extra_state = ComputeExtraICState(strict_mode);
1349 Handle<Code> ic =
1350 PropertyICCompiler::ComputeStore(isolate, UNINITIALIZED, extra_state);
1351 return ic;
1352 }
1353
1354
megamorphic_stub()1355 Handle<Code> StoreIC::megamorphic_stub() {
1356 if (kind() == Code::STORE_IC) {
1357 return PropertyICCompiler::ComputeStore(isolate(), MEGAMORPHIC,
1358 extra_ic_state());
1359 } else {
1360 DCHECK(kind() == Code::KEYED_STORE_IC);
1361 if (strict_mode() == STRICT) {
1362 return isolate()->builtins()->KeyedStoreIC_Generic_Strict();
1363 } else {
1364 return isolate()->builtins()->KeyedStoreIC_Generic();
1365 }
1366 }
1367 }
1368
1369
generic_stub() const1370 Handle<Code> StoreIC::generic_stub() const {
1371 if (kind() == Code::STORE_IC) {
1372 return PropertyICCompiler::ComputeStore(isolate(), GENERIC,
1373 extra_ic_state());
1374 } else {
1375 DCHECK(kind() == Code::KEYED_STORE_IC);
1376 if (strict_mode() == STRICT) {
1377 return isolate()->builtins()->KeyedStoreIC_Generic_Strict();
1378 } else {
1379 return isolate()->builtins()->KeyedStoreIC_Generic();
1380 }
1381 }
1382 }
1383
1384
slow_stub() const1385 Handle<Code> StoreIC::slow_stub() const {
1386 if (kind() == Code::STORE_IC) {
1387 return isolate()->builtins()->StoreIC_Slow();
1388 } else {
1389 DCHECK(kind() == Code::KEYED_STORE_IC);
1390 return isolate()->builtins()->KeyedStoreIC_Slow();
1391 }
1392 }
1393
1394
pre_monomorphic_stub(Isolate * isolate,StrictMode strict_mode)1395 Handle<Code> StoreIC::pre_monomorphic_stub(Isolate* isolate,
1396 StrictMode strict_mode) {
1397 ExtraICState state = ComputeExtraICState(strict_mode);
1398 return PropertyICCompiler::ComputeStore(isolate, PREMONOMORPHIC, state);
1399 }
1400
1401
UpdateCaches(LookupIterator * lookup,Handle<Object> value,JSReceiver::StoreFromKeyed store_mode)1402 void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
1403 JSReceiver::StoreFromKeyed store_mode) {
1404 if (state() == UNINITIALIZED) {
1405 // This is the first time we execute this inline cache. Set the target to
1406 // the pre monomorphic stub to delay setting the monomorphic state.
1407 set_target(*pre_monomorphic_stub());
1408 TRACE_IC("StoreIC", lookup->name());
1409 return;
1410 }
1411
1412 bool use_ic = LookupForWrite(lookup, value, store_mode);
1413 if (!use_ic) {
1414 TRACE_GENERIC_IC(isolate(), "StoreIC", "LookupForWrite said 'false'");
1415 }
1416 Handle<Code> code = use_ic ? ComputeHandler(lookup, value) : slow_stub();
1417
1418 PatchCache(lookup->name(), code);
1419 TRACE_IC("StoreIC", lookup->name());
1420 }
1421
1422
CompileHandler(LookupIterator * lookup,Handle<Object> value,CacheHolderFlag cache_holder)1423 Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
1424 Handle<Object> value,
1425 CacheHolderFlag cache_holder) {
1426 DCHECK_NE(LookupIterator::JSPROXY, lookup->state());
1427
1428 // This is currently guaranteed by checks in StoreIC::Store.
1429 Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver());
1430 Handle<JSObject> holder = lookup->GetHolder<JSObject>();
1431 DCHECK(!receiver->IsAccessCheckNeeded());
1432
1433 switch (lookup->state()) {
1434 case LookupIterator::TRANSITION: {
1435 Handle<Map> transition = lookup->transition_map();
1436 // Currently not handled by CompileStoreTransition.
1437 if (!holder->HasFastProperties()) {
1438 TRACE_GENERIC_IC(isolate(), "StoreIC", "transition from slow");
1439 break;
1440 }
1441
1442 DCHECK(lookup->IsCacheableTransition());
1443 NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
1444 return compiler.CompileStoreTransition(transition, lookup->name());
1445 }
1446
1447 case LookupIterator::INTERCEPTOR: {
1448 DCHECK(!holder->GetNamedInterceptor()->setter()->IsUndefined());
1449 NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
1450 return compiler.CompileStoreInterceptor(lookup->name());
1451 }
1452
1453 case LookupIterator::ACCESSOR: {
1454 if (!holder->HasFastProperties()) {
1455 TRACE_GENERIC_IC(isolate(), "StoreIC", "accessor on slow map");
1456 break;
1457 }
1458 Handle<Object> accessors = lookup->GetAccessors();
1459 if (accessors->IsExecutableAccessorInfo()) {
1460 Handle<ExecutableAccessorInfo> info =
1461 Handle<ExecutableAccessorInfo>::cast(accessors);
1462 if (v8::ToCData<Address>(info->setter()) == 0) {
1463 TRACE_GENERIC_IC(isolate(), "StoreIC", "setter == 0");
1464 break;
1465 }
1466 if (!ExecutableAccessorInfo::IsCompatibleReceiverType(
1467 isolate(), info, receiver_type())) {
1468 TRACE_GENERIC_IC(isolate(), "StoreIC", "incompatible receiver type");
1469 break;
1470 }
1471 NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
1472 return compiler.CompileStoreCallback(receiver, lookup->name(), info);
1473 } else if (accessors->IsAccessorPair()) {
1474 Handle<Object> setter(Handle<AccessorPair>::cast(accessors)->setter(),
1475 isolate());
1476 if (!setter->IsJSFunction()) {
1477 TRACE_GENERIC_IC(isolate(), "StoreIC", "setter not a function");
1478 break;
1479 }
1480 Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
1481 CallOptimization call_optimization(function);
1482 NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
1483 if (call_optimization.is_simple_api_call() &&
1484 call_optimization.IsCompatibleReceiver(receiver, holder)) {
1485 return compiler.CompileStoreCallback(receiver, lookup->name(),
1486 call_optimization);
1487 }
1488 return compiler.CompileStoreViaSetter(receiver, lookup->name(),
1489 Handle<JSFunction>::cast(setter));
1490 }
1491 // TODO(dcarney): Handle correctly.
1492 DCHECK(accessors->IsDeclaredAccessorInfo());
1493 TRACE_GENERIC_IC(isolate(), "StoreIC", "declared accessor info");
1494 break;
1495 }
1496
1497 case LookupIterator::DATA: {
1498 if (lookup->is_dictionary_holder()) {
1499 if (holder->IsGlobalObject()) {
1500 Handle<PropertyCell> cell = lookup->GetPropertyCell();
1501 Handle<HeapType> union_type = PropertyCell::UpdatedType(cell, value);
1502 StoreGlobalStub stub(isolate(), union_type->IsConstant(),
1503 receiver->IsJSGlobalProxy());
1504 Handle<Code> code = stub.GetCodeCopyFromTemplate(
1505 Handle<GlobalObject>::cast(holder), cell);
1506 // TODO(verwaest): Move caching of these NORMAL stubs outside as well.
1507 HeapObject::UpdateMapCodeCache(receiver, lookup->name(), code);
1508 return code;
1509 }
1510 DCHECK(holder.is_identical_to(receiver));
1511 return isolate()->builtins()->StoreIC_Normal();
1512 }
1513
1514 // -------------- Fields --------------
1515 if (lookup->property_details().type() == FIELD) {
1516 bool use_stub = true;
1517 if (lookup->representation().IsHeapObject()) {
1518 // Only use a generic stub if no types need to be tracked.
1519 Handle<HeapType> field_type = lookup->GetFieldType();
1520 HeapType::Iterator<Map> it = field_type->Classes();
1521 use_stub = it.Done();
1522 }
1523 if (use_stub) {
1524 StoreFieldStub stub(isolate(), lookup->GetFieldIndex(),
1525 lookup->representation());
1526 return stub.GetCode();
1527 }
1528 NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
1529 return compiler.CompileStoreField(lookup);
1530 }
1531
1532 // -------------- Constant properties --------------
1533 DCHECK(lookup->property_details().type() == CONSTANT);
1534 TRACE_GENERIC_IC(isolate(), "StoreIC", "constant property");
1535 break;
1536 }
1537
1538 case LookupIterator::ACCESS_CHECK:
1539 case LookupIterator::JSPROXY:
1540 case LookupIterator::NOT_FOUND:
1541 UNREACHABLE();
1542 }
1543 return slow_stub();
1544 }
1545
1546
StoreElementStub(Handle<JSObject> receiver,KeyedAccessStoreMode store_mode)1547 Handle<Code> KeyedStoreIC::StoreElementStub(Handle<JSObject> receiver,
1548 KeyedAccessStoreMode store_mode) {
1549 // Don't handle megamorphic property accesses for INTERCEPTORS or CALLBACKS
1550 // via megamorphic stubs, since they don't have a map in their relocation info
1551 // and so the stubs can't be harvested for the object needed for a map check.
1552 if (target()->type() != Code::NORMAL) {
1553 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "non-NORMAL target type");
1554 return generic_stub();
1555 }
1556
1557 Handle<Map> receiver_map(receiver->map(), isolate());
1558 MapHandleList target_receiver_maps;
1559 TargetMaps(&target_receiver_maps);
1560 if (target_receiver_maps.length() == 0) {
1561 Handle<Map> monomorphic_map =
1562 ComputeTransitionedMap(receiver_map, store_mode);
1563 store_mode = GetNonTransitioningStoreMode(store_mode);
1564 return PropertyICCompiler::ComputeKeyedStoreMonomorphic(
1565 monomorphic_map, strict_mode(), store_mode);
1566 }
1567
1568 // There are several special cases where an IC that is MONOMORPHIC can still
1569 // transition to a different GetNonTransitioningStoreMode IC that handles a
1570 // superset of the original IC. Handle those here if the receiver map hasn't
1571 // changed or it has transitioned to a more general kind.
1572 KeyedAccessStoreMode old_store_mode =
1573 KeyedStoreIC::GetKeyedAccessStoreMode(target()->extra_ic_state());
1574 Handle<Map> previous_receiver_map = target_receiver_maps.at(0);
1575 if (state() == MONOMORPHIC) {
1576 Handle<Map> transitioned_receiver_map = receiver_map;
1577 if (IsTransitionStoreMode(store_mode)) {
1578 transitioned_receiver_map =
1579 ComputeTransitionedMap(receiver_map, store_mode);
1580 }
1581 if ((receiver_map.is_identical_to(previous_receiver_map) &&
1582 IsTransitionStoreMode(store_mode)) ||
1583 IsTransitionOfMonomorphicTarget(*previous_receiver_map,
1584 *transitioned_receiver_map)) {
1585 // If the "old" and "new" maps are in the same elements map family, or
1586 // if they at least come from the same origin for a transitioning store,
1587 // stay MONOMORPHIC and use the map for the most generic ElementsKind.
1588 store_mode = GetNonTransitioningStoreMode(store_mode);
1589 return PropertyICCompiler::ComputeKeyedStoreMonomorphic(
1590 transitioned_receiver_map, strict_mode(), store_mode);
1591 } else if (*previous_receiver_map == receiver->map() &&
1592 old_store_mode == STANDARD_STORE &&
1593 (store_mode == STORE_AND_GROW_NO_TRANSITION ||
1594 store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS ||
1595 store_mode == STORE_NO_TRANSITION_HANDLE_COW)) {
1596 // A "normal" IC that handles stores can switch to a version that can
1597 // grow at the end of the array, handle OOB accesses or copy COW arrays
1598 // and still stay MONOMORPHIC.
1599 return PropertyICCompiler::ComputeKeyedStoreMonomorphic(
1600 receiver_map, strict_mode(), store_mode);
1601 }
1602 }
1603
1604 DCHECK(state() != GENERIC);
1605
1606 bool map_added =
1607 AddOneReceiverMapIfMissing(&target_receiver_maps, receiver_map);
1608
1609 if (IsTransitionStoreMode(store_mode)) {
1610 Handle<Map> transitioned_receiver_map =
1611 ComputeTransitionedMap(receiver_map, store_mode);
1612 map_added |= AddOneReceiverMapIfMissing(&target_receiver_maps,
1613 transitioned_receiver_map);
1614 }
1615
1616 if (!map_added) {
1617 // If the miss wasn't due to an unseen map, a polymorphic stub
1618 // won't help, use the generic stub.
1619 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "same map added twice");
1620 return generic_stub();
1621 }
1622
1623 // If the maximum number of receiver maps has been exceeded, use the generic
1624 // version of the IC.
1625 if (target_receiver_maps.length() > kMaxKeyedPolymorphism) {
1626 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "max polymorph exceeded");
1627 return generic_stub();
1628 }
1629
1630 // Make sure all polymorphic handlers have the same store mode, otherwise the
1631 // generic stub must be used.
1632 store_mode = GetNonTransitioningStoreMode(store_mode);
1633 if (old_store_mode != STANDARD_STORE) {
1634 if (store_mode == STANDARD_STORE) {
1635 store_mode = old_store_mode;
1636 } else if (store_mode != old_store_mode) {
1637 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "store mode mismatch");
1638 return generic_stub();
1639 }
1640 }
1641
1642 // If the store mode isn't the standard mode, make sure that all polymorphic
1643 // receivers are either external arrays, or all "normal" arrays. Otherwise,
1644 // use the generic stub.
1645 if (store_mode != STANDARD_STORE) {
1646 int external_arrays = 0;
1647 for (int i = 0; i < target_receiver_maps.length(); ++i) {
1648 if (target_receiver_maps[i]->has_external_array_elements() ||
1649 target_receiver_maps[i]->has_fixed_typed_array_elements()) {
1650 external_arrays++;
1651 }
1652 }
1653 if (external_arrays != 0 &&
1654 external_arrays != target_receiver_maps.length()) {
1655 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC",
1656 "unsupported combination of external and normal arrays");
1657 return generic_stub();
1658 }
1659 }
1660
1661 return PropertyICCompiler::ComputeKeyedStorePolymorphic(
1662 &target_receiver_maps, store_mode, strict_mode());
1663 }
1664
1665
ComputeTransitionedMap(Handle<Map> map,KeyedAccessStoreMode store_mode)1666 Handle<Map> KeyedStoreIC::ComputeTransitionedMap(
1667 Handle<Map> map, KeyedAccessStoreMode store_mode) {
1668 switch (store_mode) {
1669 case STORE_TRANSITION_SMI_TO_OBJECT:
1670 case STORE_TRANSITION_DOUBLE_TO_OBJECT:
1671 case STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT:
1672 case STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT:
1673 return Map::TransitionElementsTo(map, FAST_ELEMENTS);
1674 case STORE_TRANSITION_SMI_TO_DOUBLE:
1675 case STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE:
1676 return Map::TransitionElementsTo(map, FAST_DOUBLE_ELEMENTS);
1677 case STORE_TRANSITION_HOLEY_SMI_TO_OBJECT:
1678 case STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT:
1679 case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT:
1680 case STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT:
1681 return Map::TransitionElementsTo(map, FAST_HOLEY_ELEMENTS);
1682 case STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE:
1683 case STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE:
1684 return Map::TransitionElementsTo(map, FAST_HOLEY_DOUBLE_ELEMENTS);
1685 case STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS:
1686 DCHECK(map->has_external_array_elements());
1687 // Fall through
1688 case STORE_NO_TRANSITION_HANDLE_COW:
1689 case STANDARD_STORE:
1690 case STORE_AND_GROW_NO_TRANSITION:
1691 return map;
1692 }
1693 UNREACHABLE();
1694 return MaybeHandle<Map>().ToHandleChecked();
1695 }
1696
1697
IsOutOfBoundsAccess(Handle<JSObject> receiver,int index)1698 bool IsOutOfBoundsAccess(Handle<JSObject> receiver, int index) {
1699 if (receiver->IsJSArray()) {
1700 return JSArray::cast(*receiver)->length()->IsSmi() &&
1701 index >= Smi::cast(JSArray::cast(*receiver)->length())->value();
1702 }
1703 return index >= receiver->elements()->length();
1704 }
1705
1706
GetStoreMode(Handle<JSObject> receiver,Handle<Object> key,Handle<Object> value)1707 KeyedAccessStoreMode KeyedStoreIC::GetStoreMode(Handle<JSObject> receiver,
1708 Handle<Object> key,
1709 Handle<Object> value) {
1710 Handle<Smi> smi_key = Object::ToSmi(isolate(), key).ToHandleChecked();
1711 int index = smi_key->value();
1712 bool oob_access = IsOutOfBoundsAccess(receiver, index);
1713 // Don't consider this a growing store if the store would send the receiver to
1714 // dictionary mode.
1715 bool allow_growth = receiver->IsJSArray() && oob_access &&
1716 !receiver->WouldConvertToSlowElements(key);
1717 if (allow_growth) {
1718 // Handle growing array in stub if necessary.
1719 if (receiver->HasFastSmiElements()) {
1720 if (value->IsHeapNumber()) {
1721 if (receiver->HasFastHoleyElements()) {
1722 return STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_DOUBLE;
1723 } else {
1724 return STORE_AND_GROW_TRANSITION_SMI_TO_DOUBLE;
1725 }
1726 }
1727 if (value->IsHeapObject()) {
1728 if (receiver->HasFastHoleyElements()) {
1729 return STORE_AND_GROW_TRANSITION_HOLEY_SMI_TO_OBJECT;
1730 } else {
1731 return STORE_AND_GROW_TRANSITION_SMI_TO_OBJECT;
1732 }
1733 }
1734 } else if (receiver->HasFastDoubleElements()) {
1735 if (!value->IsSmi() && !value->IsHeapNumber()) {
1736 if (receiver->HasFastHoleyElements()) {
1737 return STORE_AND_GROW_TRANSITION_HOLEY_DOUBLE_TO_OBJECT;
1738 } else {
1739 return STORE_AND_GROW_TRANSITION_DOUBLE_TO_OBJECT;
1740 }
1741 }
1742 }
1743 return STORE_AND_GROW_NO_TRANSITION;
1744 } else {
1745 // Handle only in-bounds elements accesses.
1746 if (receiver->HasFastSmiElements()) {
1747 if (value->IsHeapNumber()) {
1748 if (receiver->HasFastHoleyElements()) {
1749 return STORE_TRANSITION_HOLEY_SMI_TO_DOUBLE;
1750 } else {
1751 return STORE_TRANSITION_SMI_TO_DOUBLE;
1752 }
1753 } else if (value->IsHeapObject()) {
1754 if (receiver->HasFastHoleyElements()) {
1755 return STORE_TRANSITION_HOLEY_SMI_TO_OBJECT;
1756 } else {
1757 return STORE_TRANSITION_SMI_TO_OBJECT;
1758 }
1759 }
1760 } else if (receiver->HasFastDoubleElements()) {
1761 if (!value->IsSmi() && !value->IsHeapNumber()) {
1762 if (receiver->HasFastHoleyElements()) {
1763 return STORE_TRANSITION_HOLEY_DOUBLE_TO_OBJECT;
1764 } else {
1765 return STORE_TRANSITION_DOUBLE_TO_OBJECT;
1766 }
1767 }
1768 }
1769 if (!FLAG_trace_external_array_abuse &&
1770 receiver->map()->has_external_array_elements() && oob_access) {
1771 return STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS;
1772 }
1773 Heap* heap = receiver->GetHeap();
1774 if (receiver->elements()->map() == heap->fixed_cow_array_map()) {
1775 return STORE_NO_TRANSITION_HANDLE_COW;
1776 } else {
1777 return STANDARD_STORE;
1778 }
1779 }
1780 }
1781
1782
Store(Handle<Object> object,Handle<Object> key,Handle<Object> value)1783 MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
1784 Handle<Object> key,
1785 Handle<Object> value) {
1786 // TODO(verwaest): Let SetProperty do the migration, since storing a property
1787 // might deprecate the current map again, if value does not fit.
1788 if (MigrateDeprecated(object)) {
1789 Handle<Object> result;
1790 ASSIGN_RETURN_ON_EXCEPTION(
1791 isolate(), result, Runtime::SetObjectProperty(isolate(), object, key,
1792 value, strict_mode()),
1793 Object);
1794 return result;
1795 }
1796
1797 // Check for non-string values that can be converted into an
1798 // internalized string directly or is representable as a smi.
1799 key = TryConvertKey(key, isolate());
1800
1801 Handle<Object> store_handle;
1802 Handle<Code> stub = generic_stub();
1803
1804 if (key->IsInternalizedString()) {
1805 ASSIGN_RETURN_ON_EXCEPTION(
1806 isolate(), store_handle,
1807 StoreIC::Store(object, Handle<String>::cast(key), value,
1808 JSReceiver::MAY_BE_STORE_FROM_KEYED),
1809 Object);
1810 // TODO(jkummerow): Ideally we'd wrap this in "if (!is_target_set())",
1811 // but doing so causes Hydrogen crashes. Needs investigation.
1812 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC",
1813 "unhandled internalized string key");
1814 TRACE_IC("StoreIC", key);
1815 set_target(*stub);
1816 return store_handle;
1817 }
1818
1819 bool use_ic =
1820 FLAG_use_ic && !object->IsStringWrapper() &&
1821 !object->IsAccessCheckNeeded() && !object->IsJSGlobalProxy() &&
1822 !(object->IsJSObject() && JSObject::cast(*object)->map()->is_observed());
1823 if (use_ic && !object->IsSmi()) {
1824 // Don't use ICs for maps of the objects in Array's prototype chain. We
1825 // expect to be able to trap element sets to objects with those maps in
1826 // the runtime to enable optimization of element hole access.
1827 Handle<HeapObject> heap_object = Handle<HeapObject>::cast(object);
1828 if (heap_object->map()->IsMapInArrayPrototypeChain()) {
1829 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "map in array prototype");
1830 use_ic = false;
1831 }
1832 }
1833
1834 if (use_ic) {
1835 DCHECK(!object->IsAccessCheckNeeded());
1836
1837 if (object->IsJSObject()) {
1838 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1839 bool key_is_smi_like = !Object::ToSmi(isolate(), key).is_null();
1840 if (receiver->elements()->map() ==
1841 isolate()->heap()->sloppy_arguments_elements_map()) {
1842 if (strict_mode() == SLOPPY) {
1843 stub = sloppy_arguments_stub();
1844 } else {
1845 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "arguments receiver");
1846 }
1847 } else if (key_is_smi_like &&
1848 !(target().is_identical_to(sloppy_arguments_stub()))) {
1849 // We should go generic if receiver isn't a dictionary, but our
1850 // prototype chain does have dictionary elements. This ensures that
1851 // other non-dictionary receivers in the polymorphic case benefit
1852 // from fast path keyed stores.
1853 if (!(receiver->map()->DictionaryElementsInPrototypeChainOnly())) {
1854 KeyedAccessStoreMode store_mode = GetStoreMode(receiver, key, value);
1855 stub = StoreElementStub(receiver, store_mode);
1856 } else {
1857 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "dictionary prototype");
1858 }
1859 } else {
1860 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "non-smi-like key");
1861 }
1862 } else {
1863 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "non-JSObject receiver");
1864 }
1865 }
1866
1867 if (store_handle.is_null()) {
1868 ASSIGN_RETURN_ON_EXCEPTION(
1869 isolate(), store_handle,
1870 Runtime::SetObjectProperty(isolate(), object, key, value,
1871 strict_mode()),
1872 Object);
1873 }
1874
1875 DCHECK(!is_target_set());
1876 Code* generic = *generic_stub();
1877 if (*stub == generic) {
1878 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "set generic");
1879 }
1880 if (*stub == *slow_stub()) {
1881 TRACE_GENERIC_IC(isolate(), "KeyedStoreIC", "slow stub");
1882 }
1883 DCHECK(!stub.is_null());
1884 set_target(*stub);
1885 TRACE_IC("StoreIC", key);
1886
1887 return store_handle;
1888 }
1889
1890
DoCustomHandler(Handle<Object> receiver,Handle<Object> function,Handle<TypeFeedbackVector> vector,Handle<Smi> slot,const CallICState & state)1891 bool CallIC::DoCustomHandler(Handle<Object> receiver, Handle<Object> function,
1892 Handle<TypeFeedbackVector> vector,
1893 Handle<Smi> slot, const CallICState& state) {
1894 DCHECK(FLAG_use_ic && function->IsJSFunction());
1895
1896 // Are we the array function?
1897 Handle<JSFunction> array_function =
1898 Handle<JSFunction>(isolate()->native_context()->array_function());
1899 if (array_function.is_identical_to(Handle<JSFunction>::cast(function))) {
1900 // Alter the slot.
1901 IC::State old_state = FeedbackToState(vector, slot);
1902 Object* feedback = vector->get(slot->value());
1903 if (!feedback->IsAllocationSite()) {
1904 Handle<AllocationSite> new_site =
1905 isolate()->factory()->NewAllocationSite();
1906 vector->set(slot->value(), *new_site);
1907 }
1908
1909 CallIC_ArrayStub stub(isolate(), state);
1910 set_target(*stub.GetCode());
1911 Handle<String> name;
1912 if (array_function->shared()->name()->IsString()) {
1913 name = Handle<String>(String::cast(array_function->shared()->name()),
1914 isolate());
1915 }
1916
1917 IC::State new_state = FeedbackToState(vector, slot);
1918 OnTypeFeedbackChanged(isolate(), address(), old_state, new_state, true);
1919 TRACE_VECTOR_IC("CallIC (custom handler)", name, old_state, new_state);
1920 return true;
1921 }
1922 return false;
1923 }
1924
1925
PatchMegamorphic(Handle<Object> function,Handle<TypeFeedbackVector> vector,Handle<Smi> slot)1926 void CallIC::PatchMegamorphic(Handle<Object> function,
1927 Handle<TypeFeedbackVector> vector,
1928 Handle<Smi> slot) {
1929 CallICState state(target()->extra_ic_state());
1930 IC::State old_state = FeedbackToState(vector, slot);
1931
1932 // We are going generic.
1933 vector->set(slot->value(),
1934 *TypeFeedbackVector::MegamorphicSentinel(isolate()),
1935 SKIP_WRITE_BARRIER);
1936
1937 CallICStub stub(isolate(), state);
1938 Handle<Code> code = stub.GetCode();
1939 set_target(*code);
1940
1941 Handle<Object> name = isolate()->factory()->empty_string();
1942 if (function->IsJSFunction()) {
1943 Handle<JSFunction> js_function = Handle<JSFunction>::cast(function);
1944 name = handle(js_function->shared()->name(), isolate());
1945 }
1946
1947 IC::State new_state = FeedbackToState(vector, slot);
1948 OnTypeFeedbackChanged(isolate(), address(), old_state, new_state, true);
1949 TRACE_VECTOR_IC("CallIC", name, old_state, new_state);
1950 }
1951
1952
HandleMiss(Handle<Object> receiver,Handle<Object> function,Handle<TypeFeedbackVector> vector,Handle<Smi> slot)1953 void CallIC::HandleMiss(Handle<Object> receiver, Handle<Object> function,
1954 Handle<TypeFeedbackVector> vector, Handle<Smi> slot) {
1955 CallICState state(target()->extra_ic_state());
1956 IC::State old_state = FeedbackToState(vector, slot);
1957 Handle<Object> name = isolate()->factory()->empty_string();
1958 Object* feedback = vector->get(slot->value());
1959
1960 // Hand-coded MISS handling is easier if CallIC slots don't contain smis.
1961 DCHECK(!feedback->IsSmi());
1962
1963 if (feedback->IsJSFunction() || !function->IsJSFunction()) {
1964 // We are going generic.
1965 vector->set(slot->value(),
1966 *TypeFeedbackVector::MegamorphicSentinel(isolate()),
1967 SKIP_WRITE_BARRIER);
1968 } else {
1969 // The feedback is either uninitialized or an allocation site.
1970 // It might be an allocation site because if we re-compile the full code
1971 // to add deoptimization support, we call with the default call-ic, and
1972 // merely need to patch the target to match the feedback.
1973 // TODO(mvstanton): the better approach is to dispense with patching
1974 // altogether, which is in progress.
1975 DCHECK(feedback == *TypeFeedbackVector::UninitializedSentinel(isolate()) ||
1976 feedback->IsAllocationSite());
1977
1978 // Do we want to install a custom handler?
1979 if (FLAG_use_ic &&
1980 DoCustomHandler(receiver, function, vector, slot, state)) {
1981 return;
1982 }
1983
1984 vector->set(slot->value(), *function);
1985 }
1986
1987 if (function->IsJSFunction()) {
1988 Handle<JSFunction> js_function = Handle<JSFunction>::cast(function);
1989 name = handle(js_function->shared()->name(), isolate());
1990 }
1991
1992 IC::State new_state = FeedbackToState(vector, slot);
1993 OnTypeFeedbackChanged(isolate(), address(), old_state, new_state, true);
1994 TRACE_VECTOR_IC("CallIC", name, old_state, new_state);
1995 }
1996
1997
1998 #undef TRACE_IC
1999
2000
2001 // ----------------------------------------------------------------------------
2002 // Static IC stub generators.
2003 //
2004
2005 // Used from ic-<arch>.cc.
RUNTIME_FUNCTION(CallIC_Miss)2006 RUNTIME_FUNCTION(CallIC_Miss) {
2007 TimerEventScope<TimerEventIcMiss> timer(isolate);
2008 HandleScope scope(isolate);
2009 DCHECK(args.length() == 4);
2010 CallIC ic(isolate);
2011 Handle<Object> receiver = args.at<Object>(0);
2012 Handle<Object> function = args.at<Object>(1);
2013 Handle<TypeFeedbackVector> vector = args.at<TypeFeedbackVector>(2);
2014 Handle<Smi> slot = args.at<Smi>(3);
2015 ic.HandleMiss(receiver, function, vector, slot);
2016 return *function;
2017 }
2018
2019
RUNTIME_FUNCTION(CallIC_Customization_Miss)2020 RUNTIME_FUNCTION(CallIC_Customization_Miss) {
2021 TimerEventScope<TimerEventIcMiss> timer(isolate);
2022 HandleScope scope(isolate);
2023 DCHECK(args.length() == 4);
2024 // A miss on a custom call ic always results in going megamorphic.
2025 CallIC ic(isolate);
2026 Handle<Object> function = args.at<Object>(1);
2027 Handle<TypeFeedbackVector> vector = args.at<TypeFeedbackVector>(2);
2028 Handle<Smi> slot = args.at<Smi>(3);
2029 ic.PatchMegamorphic(function, vector, slot);
2030 return *function;
2031 }
2032
2033
2034 // Used from ic-<arch>.cc.
RUNTIME_FUNCTION(LoadIC_Miss)2035 RUNTIME_FUNCTION(LoadIC_Miss) {
2036 TimerEventScope<TimerEventIcMiss> timer(isolate);
2037 HandleScope scope(isolate);
2038 DCHECK(args.length() == 2);
2039 LoadIC ic(IC::NO_EXTRA_FRAME, isolate);
2040 Handle<Object> receiver = args.at<Object>(0);
2041 Handle<Name> key = args.at<Name>(1);
2042 ic.UpdateState(receiver, key);
2043 Handle<Object> result;
2044 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, ic.Load(receiver, key));
2045 return *result;
2046 }
2047
2048
2049 // Used from ic-<arch>.cc
RUNTIME_FUNCTION(KeyedLoadIC_Miss)2050 RUNTIME_FUNCTION(KeyedLoadIC_Miss) {
2051 TimerEventScope<TimerEventIcMiss> timer(isolate);
2052 HandleScope scope(isolate);
2053 DCHECK(args.length() == 2);
2054 KeyedLoadIC ic(IC::NO_EXTRA_FRAME, isolate);
2055 Handle<Object> receiver = args.at<Object>(0);
2056 Handle<Object> key = args.at<Object>(1);
2057 ic.UpdateState(receiver, key);
2058 Handle<Object> result;
2059 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, ic.Load(receiver, key));
2060 return *result;
2061 }
2062
2063
RUNTIME_FUNCTION(KeyedLoadIC_MissFromStubFailure)2064 RUNTIME_FUNCTION(KeyedLoadIC_MissFromStubFailure) {
2065 TimerEventScope<TimerEventIcMiss> timer(isolate);
2066 HandleScope scope(isolate);
2067 DCHECK(args.length() == 2);
2068 KeyedLoadIC ic(IC::EXTRA_CALL_FRAME, isolate);
2069 Handle<Object> receiver = args.at<Object>(0);
2070 Handle<Object> key = args.at<Object>(1);
2071 ic.UpdateState(receiver, key);
2072 Handle<Object> result;
2073 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, ic.Load(receiver, key));
2074 return *result;
2075 }
2076
2077
2078 // Used from ic-<arch>.cc.
RUNTIME_FUNCTION(StoreIC_Miss)2079 RUNTIME_FUNCTION(StoreIC_Miss) {
2080 TimerEventScope<TimerEventIcMiss> timer(isolate);
2081 HandleScope scope(isolate);
2082 DCHECK(args.length() == 3);
2083 StoreIC ic(IC::NO_EXTRA_FRAME, isolate);
2084 Handle<Object> receiver = args.at<Object>(0);
2085 Handle<String> key = args.at<String>(1);
2086 ic.UpdateState(receiver, key);
2087 Handle<Object> result;
2088 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2089 isolate, result, ic.Store(receiver, key, args.at<Object>(2)));
2090 return *result;
2091 }
2092
2093
RUNTIME_FUNCTION(StoreIC_MissFromStubFailure)2094 RUNTIME_FUNCTION(StoreIC_MissFromStubFailure) {
2095 TimerEventScope<TimerEventIcMiss> timer(isolate);
2096 HandleScope scope(isolate);
2097 DCHECK(args.length() == 3);
2098 StoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
2099 Handle<Object> receiver = args.at<Object>(0);
2100 Handle<String> key = args.at<String>(1);
2101 ic.UpdateState(receiver, key);
2102 Handle<Object> result;
2103 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2104 isolate, result, ic.Store(receiver, key, args.at<Object>(2)));
2105 return *result;
2106 }
2107
2108
2109 // Extend storage is called in a store inline cache when
2110 // it is necessary to extend the properties array of a
2111 // JSObject.
RUNTIME_FUNCTION(SharedStoreIC_ExtendStorage)2112 RUNTIME_FUNCTION(SharedStoreIC_ExtendStorage) {
2113 TimerEventScope<TimerEventIcMiss> timer(isolate);
2114 HandleScope shs(isolate);
2115 DCHECK(args.length() == 3);
2116
2117 // Convert the parameters
2118 Handle<JSObject> object = args.at<JSObject>(0);
2119 Handle<Map> transition = args.at<Map>(1);
2120 Handle<Object> value = args.at<Object>(2);
2121
2122 // Check the object has run out out property space.
2123 DCHECK(object->HasFastProperties());
2124 DCHECK(object->map()->unused_property_fields() == 0);
2125
2126 JSObject::MigrateToNewProperty(object, transition, value);
2127
2128 // Return the stored value.
2129 return *value;
2130 }
2131
2132
2133 // Used from ic-<arch>.cc.
RUNTIME_FUNCTION(KeyedStoreIC_Miss)2134 RUNTIME_FUNCTION(KeyedStoreIC_Miss) {
2135 TimerEventScope<TimerEventIcMiss> timer(isolate);
2136 HandleScope scope(isolate);
2137 DCHECK(args.length() == 3);
2138 KeyedStoreIC ic(IC::NO_EXTRA_FRAME, isolate);
2139 Handle<Object> receiver = args.at<Object>(0);
2140 Handle<Object> key = args.at<Object>(1);
2141 ic.UpdateState(receiver, key);
2142 Handle<Object> result;
2143 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2144 isolate, result, ic.Store(receiver, key, args.at<Object>(2)));
2145 return *result;
2146 }
2147
2148
RUNTIME_FUNCTION(KeyedStoreIC_MissFromStubFailure)2149 RUNTIME_FUNCTION(KeyedStoreIC_MissFromStubFailure) {
2150 TimerEventScope<TimerEventIcMiss> timer(isolate);
2151 HandleScope scope(isolate);
2152 DCHECK(args.length() == 3);
2153 KeyedStoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
2154 Handle<Object> receiver = args.at<Object>(0);
2155 Handle<Object> key = args.at<Object>(1);
2156 ic.UpdateState(receiver, key);
2157 Handle<Object> result;
2158 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2159 isolate, result, ic.Store(receiver, key, args.at<Object>(2)));
2160 return *result;
2161 }
2162
2163
RUNTIME_FUNCTION(StoreIC_Slow)2164 RUNTIME_FUNCTION(StoreIC_Slow) {
2165 HandleScope scope(isolate);
2166 DCHECK(args.length() == 3);
2167 StoreIC ic(IC::NO_EXTRA_FRAME, isolate);
2168 Handle<Object> object = args.at<Object>(0);
2169 Handle<Object> key = args.at<Object>(1);
2170 Handle<Object> value = args.at<Object>(2);
2171 StrictMode strict_mode = ic.strict_mode();
2172 Handle<Object> result;
2173 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2174 isolate, result,
2175 Runtime::SetObjectProperty(isolate, object, key, value, strict_mode));
2176 return *result;
2177 }
2178
2179
RUNTIME_FUNCTION(KeyedStoreIC_Slow)2180 RUNTIME_FUNCTION(KeyedStoreIC_Slow) {
2181 HandleScope scope(isolate);
2182 DCHECK(args.length() == 3);
2183 KeyedStoreIC ic(IC::NO_EXTRA_FRAME, isolate);
2184 Handle<Object> object = args.at<Object>(0);
2185 Handle<Object> key = args.at<Object>(1);
2186 Handle<Object> value = args.at<Object>(2);
2187 StrictMode strict_mode = ic.strict_mode();
2188 Handle<Object> result;
2189 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2190 isolate, result,
2191 Runtime::SetObjectProperty(isolate, object, key, value, strict_mode));
2192 return *result;
2193 }
2194
2195
RUNTIME_FUNCTION(ElementsTransitionAndStoreIC_Miss)2196 RUNTIME_FUNCTION(ElementsTransitionAndStoreIC_Miss) {
2197 TimerEventScope<TimerEventIcMiss> timer(isolate);
2198 HandleScope scope(isolate);
2199 DCHECK(args.length() == 4);
2200 KeyedStoreIC ic(IC::EXTRA_CALL_FRAME, isolate);
2201 Handle<Object> value = args.at<Object>(0);
2202 Handle<Map> map = args.at<Map>(1);
2203 Handle<Object> key = args.at<Object>(2);
2204 Handle<Object> object = args.at<Object>(3);
2205 StrictMode strict_mode = ic.strict_mode();
2206 if (object->IsJSObject()) {
2207 JSObject::TransitionElementsKind(Handle<JSObject>::cast(object),
2208 map->elements_kind());
2209 }
2210 Handle<Object> result;
2211 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2212 isolate, result,
2213 Runtime::SetObjectProperty(isolate, object, key, value, strict_mode));
2214 return *result;
2215 }
2216
2217
Transition(Handle<AllocationSite> allocation_site,Handle<Object> left,Handle<Object> right)2218 MaybeHandle<Object> BinaryOpIC::Transition(
2219 Handle<AllocationSite> allocation_site, Handle<Object> left,
2220 Handle<Object> right) {
2221 BinaryOpICState state(isolate(), target()->extra_ic_state());
2222
2223 // Compute the actual result using the builtin for the binary operation.
2224 Object* builtin = isolate()->js_builtins_object()->javascript_builtin(
2225 TokenToJSBuiltin(state.op()));
2226 Handle<JSFunction> function = handle(JSFunction::cast(builtin), isolate());
2227 Handle<Object> result;
2228 ASSIGN_RETURN_ON_EXCEPTION(
2229 isolate(), result, Execution::Call(isolate(), function, left, 1, &right),
2230 Object);
2231
2232 // Execution::Call can execute arbitrary JavaScript, hence potentially
2233 // update the state of this very IC, so we must update the stored state.
2234 UpdateTarget();
2235 // Compute the new state.
2236 BinaryOpICState old_state(isolate(), target()->extra_ic_state());
2237 state.Update(left, right, result);
2238
2239 // Check if we have a string operation here.
2240 Handle<Code> target;
2241 if (!allocation_site.is_null() || state.ShouldCreateAllocationMementos()) {
2242 // Setup the allocation site on-demand.
2243 if (allocation_site.is_null()) {
2244 allocation_site = isolate()->factory()->NewAllocationSite();
2245 }
2246
2247 // Install the stub with an allocation site.
2248 BinaryOpICWithAllocationSiteStub stub(isolate(), state);
2249 target = stub.GetCodeCopyFromTemplate(allocation_site);
2250
2251 // Sanity check the trampoline stub.
2252 DCHECK_EQ(*allocation_site, target->FindFirstAllocationSite());
2253 } else {
2254 // Install the generic stub.
2255 BinaryOpICStub stub(isolate(), state);
2256 target = stub.GetCode();
2257
2258 // Sanity check the generic stub.
2259 DCHECK_EQ(NULL, target->FindFirstAllocationSite());
2260 }
2261 set_target(*target);
2262
2263 if (FLAG_trace_ic) {
2264 OFStream os(stdout);
2265 os << "[BinaryOpIC" << old_state << " => " << state << " @ "
2266 << static_cast<void*>(*target) << " <- ";
2267 JavaScriptFrame::PrintTop(isolate(), stdout, false, true);
2268 if (!allocation_site.is_null()) {
2269 os << " using allocation site " << static_cast<void*>(*allocation_site);
2270 }
2271 os << "]" << endl;
2272 }
2273
2274 // Patch the inlined smi code as necessary.
2275 if (!old_state.UseInlinedSmiCode() && state.UseInlinedSmiCode()) {
2276 PatchInlinedSmiCode(address(), ENABLE_INLINED_SMI_CHECK);
2277 } else if (old_state.UseInlinedSmiCode() && !state.UseInlinedSmiCode()) {
2278 PatchInlinedSmiCode(address(), DISABLE_INLINED_SMI_CHECK);
2279 }
2280
2281 return result;
2282 }
2283
2284
RUNTIME_FUNCTION(BinaryOpIC_Miss)2285 RUNTIME_FUNCTION(BinaryOpIC_Miss) {
2286 TimerEventScope<TimerEventIcMiss> timer(isolate);
2287 HandleScope scope(isolate);
2288 DCHECK_EQ(2, args.length());
2289 Handle<Object> left = args.at<Object>(BinaryOpICStub::kLeft);
2290 Handle<Object> right = args.at<Object>(BinaryOpICStub::kRight);
2291 BinaryOpIC ic(isolate);
2292 Handle<Object> result;
2293 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2294 isolate, result,
2295 ic.Transition(Handle<AllocationSite>::null(), left, right));
2296 return *result;
2297 }
2298
2299
RUNTIME_FUNCTION(BinaryOpIC_MissWithAllocationSite)2300 RUNTIME_FUNCTION(BinaryOpIC_MissWithAllocationSite) {
2301 TimerEventScope<TimerEventIcMiss> timer(isolate);
2302 HandleScope scope(isolate);
2303 DCHECK_EQ(3, args.length());
2304 Handle<AllocationSite> allocation_site =
2305 args.at<AllocationSite>(BinaryOpWithAllocationSiteStub::kAllocationSite);
2306 Handle<Object> left = args.at<Object>(BinaryOpWithAllocationSiteStub::kLeft);
2307 Handle<Object> right =
2308 args.at<Object>(BinaryOpWithAllocationSiteStub::kRight);
2309 BinaryOpIC ic(isolate);
2310 Handle<Object> result;
2311 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2312 isolate, result, ic.Transition(allocation_site, left, right));
2313 return *result;
2314 }
2315
2316
GetRawUninitialized(Isolate * isolate,Token::Value op)2317 Code* CompareIC::GetRawUninitialized(Isolate* isolate, Token::Value op) {
2318 CompareICStub stub(isolate, op, CompareICState::UNINITIALIZED,
2319 CompareICState::UNINITIALIZED,
2320 CompareICState::UNINITIALIZED);
2321 Code* code = NULL;
2322 CHECK(stub.FindCodeInCache(&code));
2323 return code;
2324 }
2325
2326
GetUninitialized(Isolate * isolate,Token::Value op)2327 Handle<Code> CompareIC::GetUninitialized(Isolate* isolate, Token::Value op) {
2328 CompareICStub stub(isolate, op, CompareICState::UNINITIALIZED,
2329 CompareICState::UNINITIALIZED,
2330 CompareICState::UNINITIALIZED);
2331 return stub.GetCode();
2332 }
2333
2334
UpdateCaches(Handle<Object> x,Handle<Object> y)2335 Code* CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) {
2336 HandleScope scope(isolate());
2337 CompareICStub old_stub(target()->stub_key(), isolate());
2338 CompareICState::State new_left =
2339 CompareICState::NewInputState(old_stub.left(), x);
2340 CompareICState::State new_right =
2341 CompareICState::NewInputState(old_stub.right(), y);
2342 CompareICState::State state = CompareICState::TargetState(
2343 old_stub.state(), old_stub.left(), old_stub.right(), op_,
2344 HasInlinedSmiCode(address()), x, y);
2345 CompareICStub stub(isolate(), op_, new_left, new_right, state);
2346 if (state == CompareICState::KNOWN_OBJECT) {
2347 stub.set_known_map(
2348 Handle<Map>(Handle<JSObject>::cast(x)->map(), isolate()));
2349 }
2350 Handle<Code> new_target = stub.GetCode();
2351 set_target(*new_target);
2352
2353 if (FLAG_trace_ic) {
2354 PrintF("[CompareIC in ");
2355 JavaScriptFrame::PrintTop(isolate(), stdout, false, true);
2356 PrintF(" ((%s+%s=%s)->(%s+%s=%s))#%s @ %p]\n",
2357 CompareICState::GetStateName(old_stub.left()),
2358 CompareICState::GetStateName(old_stub.right()),
2359 CompareICState::GetStateName(old_stub.state()),
2360 CompareICState::GetStateName(new_left),
2361 CompareICState::GetStateName(new_right),
2362 CompareICState::GetStateName(state), Token::Name(op_),
2363 static_cast<void*>(*stub.GetCode()));
2364 }
2365
2366 // Activate inlined smi code.
2367 if (old_stub.state() == CompareICState::UNINITIALIZED) {
2368 PatchInlinedSmiCode(address(), ENABLE_INLINED_SMI_CHECK);
2369 }
2370
2371 return *new_target;
2372 }
2373
2374
2375 // Used from CompareICStub::GenerateMiss in code-stubs-<arch>.cc.
RUNTIME_FUNCTION(CompareIC_Miss)2376 RUNTIME_FUNCTION(CompareIC_Miss) {
2377 TimerEventScope<TimerEventIcMiss> timer(isolate);
2378 HandleScope scope(isolate);
2379 DCHECK(args.length() == 3);
2380 CompareIC ic(isolate, static_cast<Token::Value>(args.smi_at(2)));
2381 return ic.UpdateCaches(args.at<Object>(0), args.at<Object>(1));
2382 }
2383
2384
Clear(Address address,Code * target,ConstantPoolArray * constant_pool)2385 void CompareNilIC::Clear(Address address, Code* target,
2386 ConstantPoolArray* constant_pool) {
2387 if (IsCleared(target)) return;
2388 ExtraICState state = target->extra_ic_state();
2389
2390 CompareNilICStub stub(target->GetIsolate(), state,
2391 HydrogenCodeStub::UNINITIALIZED);
2392 stub.ClearState();
2393
2394 Code* code = NULL;
2395 CHECK(stub.FindCodeInCache(&code));
2396
2397 SetTargetAtAddress(address, code, constant_pool);
2398 }
2399
2400
DoCompareNilSlow(Isolate * isolate,NilValue nil,Handle<Object> object)2401 Handle<Object> CompareNilIC::DoCompareNilSlow(Isolate* isolate, NilValue nil,
2402 Handle<Object> object) {
2403 if (object->IsNull() || object->IsUndefined()) {
2404 return handle(Smi::FromInt(true), isolate);
2405 }
2406 return handle(Smi::FromInt(object->IsUndetectableObject()), isolate);
2407 }
2408
2409
CompareNil(Handle<Object> object)2410 Handle<Object> CompareNilIC::CompareNil(Handle<Object> object) {
2411 ExtraICState extra_ic_state = target()->extra_ic_state();
2412
2413 CompareNilICStub stub(isolate(), extra_ic_state);
2414
2415 // Extract the current supported types from the patched IC and calculate what
2416 // types must be supported as a result of the miss.
2417 bool already_monomorphic = stub.IsMonomorphic();
2418
2419 stub.UpdateStatus(object);
2420
2421 NilValue nil = stub.nil_value();
2422
2423 // Find or create the specialized stub to support the new set of types.
2424 Handle<Code> code;
2425 if (stub.IsMonomorphic()) {
2426 Handle<Map> monomorphic_map(already_monomorphic && FirstTargetMap() != NULL
2427 ? FirstTargetMap()
2428 : HeapObject::cast(*object)->map());
2429 code = PropertyICCompiler::ComputeCompareNil(monomorphic_map, &stub);
2430 } else {
2431 code = stub.GetCode();
2432 }
2433 set_target(*code);
2434 return DoCompareNilSlow(isolate(), nil, object);
2435 }
2436
2437
RUNTIME_FUNCTION(CompareNilIC_Miss)2438 RUNTIME_FUNCTION(CompareNilIC_Miss) {
2439 TimerEventScope<TimerEventIcMiss> timer(isolate);
2440 HandleScope scope(isolate);
2441 Handle<Object> object = args.at<Object>(0);
2442 CompareNilIC ic(isolate);
2443 return *ic.CompareNil(object);
2444 }
2445
2446
RUNTIME_FUNCTION(Unreachable)2447 RUNTIME_FUNCTION(Unreachable) {
2448 UNREACHABLE();
2449 CHECK(false);
2450 return isolate->heap()->undefined_value();
2451 }
2452
2453
TokenToJSBuiltin(Token::Value op)2454 Builtins::JavaScript BinaryOpIC::TokenToJSBuiltin(Token::Value op) {
2455 switch (op) {
2456 default:
2457 UNREACHABLE();
2458 case Token::ADD:
2459 return Builtins::ADD;
2460 break;
2461 case Token::SUB:
2462 return Builtins::SUB;
2463 break;
2464 case Token::MUL:
2465 return Builtins::MUL;
2466 break;
2467 case Token::DIV:
2468 return Builtins::DIV;
2469 break;
2470 case Token::MOD:
2471 return Builtins::MOD;
2472 break;
2473 case Token::BIT_OR:
2474 return Builtins::BIT_OR;
2475 break;
2476 case Token::BIT_AND:
2477 return Builtins::BIT_AND;
2478 break;
2479 case Token::BIT_XOR:
2480 return Builtins::BIT_XOR;
2481 break;
2482 case Token::SAR:
2483 return Builtins::SAR;
2484 break;
2485 case Token::SHR:
2486 return Builtins::SHR;
2487 break;
2488 case Token::SHL:
2489 return Builtins::SHL;
2490 break;
2491 }
2492 }
2493
2494
ToBoolean(Handle<Object> object)2495 Handle<Object> ToBooleanIC::ToBoolean(Handle<Object> object) {
2496 ToBooleanStub stub(isolate(), target()->extra_ic_state());
2497 bool to_boolean_value = stub.UpdateStatus(object);
2498 Handle<Code> code = stub.GetCode();
2499 set_target(*code);
2500 return handle(Smi::FromInt(to_boolean_value ? 1 : 0), isolate());
2501 }
2502
2503
RUNTIME_FUNCTION(ToBooleanIC_Miss)2504 RUNTIME_FUNCTION(ToBooleanIC_Miss) {
2505 TimerEventScope<TimerEventIcMiss> timer(isolate);
2506 DCHECK(args.length() == 1);
2507 HandleScope scope(isolate);
2508 Handle<Object> object = args.at<Object>(0);
2509 ToBooleanIC ic(isolate);
2510 return *ic.ToBoolean(object);
2511 }
2512
2513
RUNTIME_FUNCTION(StoreCallbackProperty)2514 RUNTIME_FUNCTION(StoreCallbackProperty) {
2515 Handle<JSObject> receiver = args.at<JSObject>(0);
2516 Handle<JSObject> holder = args.at<JSObject>(1);
2517 Handle<ExecutableAccessorInfo> callback = args.at<ExecutableAccessorInfo>(2);
2518 Handle<Name> name = args.at<Name>(3);
2519 Handle<Object> value = args.at<Object>(4);
2520 HandleScope scope(isolate);
2521
2522 DCHECK(callback->IsCompatibleReceiver(*receiver));
2523
2524 Address setter_address = v8::ToCData<Address>(callback->setter());
2525 v8::AccessorNameSetterCallback fun =
2526 FUNCTION_CAST<v8::AccessorNameSetterCallback>(setter_address);
2527 DCHECK(fun != NULL);
2528
2529 LOG(isolate, ApiNamedPropertyAccess("store", *receiver, *name));
2530 PropertyCallbackArguments custom_args(isolate, callback->data(), *receiver,
2531 *holder);
2532 custom_args.Call(fun, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value));
2533 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
2534 return *value;
2535 }
2536
2537
2538 /**
2539 * Attempts to load a property with an interceptor (which must be present),
2540 * but doesn't search the prototype chain.
2541 *
2542 * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't
2543 * provide any value for the given name.
2544 */
RUNTIME_FUNCTION(LoadPropertyWithInterceptorOnly)2545 RUNTIME_FUNCTION(LoadPropertyWithInterceptorOnly) {
2546 DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength);
2547 Handle<Name> name_handle =
2548 args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex);
2549 Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>(
2550 NamedLoadHandlerCompiler::kInterceptorArgsInfoIndex);
2551
2552 // TODO(rossberg): Support symbols in the API.
2553 if (name_handle->IsSymbol())
2554 return isolate->heap()->no_interceptor_result_sentinel();
2555 Handle<String> name = Handle<String>::cast(name_handle);
2556
2557 Address getter_address = v8::ToCData<Address>(interceptor_info->getter());
2558 v8::NamedPropertyGetterCallback getter =
2559 FUNCTION_CAST<v8::NamedPropertyGetterCallback>(getter_address);
2560 DCHECK(getter != NULL);
2561
2562 Handle<JSObject> receiver =
2563 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex);
2564 Handle<JSObject> holder =
2565 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex);
2566 PropertyCallbackArguments callback_args(isolate, interceptor_info->data(),
2567 *receiver, *holder);
2568 {
2569 // Use the interceptor getter.
2570 HandleScope scope(isolate);
2571 v8::Handle<v8::Value> r =
2572 callback_args.Call(getter, v8::Utils::ToLocal(name));
2573 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate);
2574 if (!r.IsEmpty()) {
2575 Handle<Object> result = v8::Utils::OpenHandle(*r);
2576 result->VerifyApiCallResultType();
2577 return *v8::Utils::OpenHandle(*r);
2578 }
2579 }
2580
2581 return isolate->heap()->no_interceptor_result_sentinel();
2582 }
2583
2584
ThrowReferenceError(Isolate * isolate,Name * name)2585 static Object* ThrowReferenceError(Isolate* isolate, Name* name) {
2586 // If the load is non-contextual, just return the undefined result.
2587 // Note that both keyed and non-keyed loads may end up here.
2588 HandleScope scope(isolate);
2589 LoadIC ic(IC::NO_EXTRA_FRAME, isolate);
2590 if (ic.contextual_mode() != CONTEXTUAL) {
2591 return isolate->heap()->undefined_value();
2592 }
2593
2594 // Throw a reference error.
2595 Handle<Name> name_handle(name);
2596 THROW_NEW_ERROR_RETURN_FAILURE(
2597 isolate, NewReferenceError("not_defined", HandleVector(&name_handle, 1)));
2598 }
2599
2600
2601 /**
2602 * Loads a property with an interceptor performing post interceptor
2603 * lookup if interceptor failed.
2604 */
RUNTIME_FUNCTION(LoadPropertyWithInterceptor)2605 RUNTIME_FUNCTION(LoadPropertyWithInterceptor) {
2606 HandleScope scope(isolate);
2607 DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength);
2608 Handle<Name> name =
2609 args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex);
2610 Handle<JSObject> receiver =
2611 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex);
2612 Handle<JSObject> holder =
2613 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex);
2614
2615 Handle<Object> result;
2616 LookupIterator it(receiver, name, holder);
2617 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
2618 JSObject::GetProperty(&it));
2619
2620 if (it.IsFound()) return *result;
2621
2622 return ThrowReferenceError(isolate, Name::cast(args[0]));
2623 }
2624
2625
RUNTIME_FUNCTION(StorePropertyWithInterceptor)2626 RUNTIME_FUNCTION(StorePropertyWithInterceptor) {
2627 HandleScope scope(isolate);
2628 DCHECK(args.length() == 3);
2629 StoreIC ic(IC::NO_EXTRA_FRAME, isolate);
2630 Handle<JSObject> receiver = args.at<JSObject>(0);
2631 Handle<Name> name = args.at<Name>(1);
2632 Handle<Object> value = args.at<Object>(2);
2633 #ifdef DEBUG
2634 PrototypeIterator iter(isolate, receiver,
2635 PrototypeIterator::START_AT_RECEIVER);
2636 bool found = false;
2637 while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) {
2638 Handle<Object> current = PrototypeIterator::GetCurrent(iter);
2639 if (current->IsJSObject() &&
2640 Handle<JSObject>::cast(current)->HasNamedInterceptor()) {
2641 found = true;
2642 break;
2643 }
2644 }
2645 DCHECK(found);
2646 #endif
2647 Handle<Object> result;
2648 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2649 isolate, result,
2650 JSObject::SetProperty(receiver, name, value, ic.strict_mode()));
2651 return *result;
2652 }
2653
2654
RUNTIME_FUNCTION(LoadElementWithInterceptor)2655 RUNTIME_FUNCTION(LoadElementWithInterceptor) {
2656 HandleScope scope(isolate);
2657 Handle<JSObject> receiver = args.at<JSObject>(0);
2658 DCHECK(args.smi_at(1) >= 0);
2659 uint32_t index = args.smi_at(1);
2660 Handle<Object> result;
2661 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
2662 isolate, result,
2663 JSObject::GetElementWithInterceptor(receiver, receiver, index));
2664 return *result;
2665 }
2666
2667
RUNTIME_FUNCTION(VectorLoadIC_MissFromStubFailure)2668 RUNTIME_FUNCTION(VectorLoadIC_MissFromStubFailure) {
2669 // TODO(mvstanton): To be enabled when ICs can accept a vector and slot
2670 return NULL;
2671 }
2672
2673
RUNTIME_FUNCTION(VectorKeyedLoadIC_MissFromStubFailure)2674 RUNTIME_FUNCTION(VectorKeyedLoadIC_MissFromStubFailure) {
2675 // TODO(mvstanton): To be enabled when ICs can accept a vector and slot
2676 return NULL;
2677 }
2678
2679
2680 static const Address IC_utilities[] = {
2681 #define ADDR(name) FUNCTION_ADDR(name),
2682 IC_UTIL_LIST(ADDR) NULL
2683 #undef ADDR
2684 };
2685
2686
AddressFromUtilityId(IC::UtilityId id)2687 Address IC::AddressFromUtilityId(IC::UtilityId id) { return IC_utilities[id]; }
2688 }
2689 } // namespace v8::internal
2690