1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/ic/handler-compiler.h"
6
7 #include "src/field-type.h"
8 #include "src/ic/call-optimization.h"
9 #include "src/ic/handler-configuration-inl.h"
10 #include "src/ic/ic-inl.h"
11 #include "src/ic/ic.h"
12 #include "src/isolate-inl.h"
13
14 namespace v8 {
15 namespace internal {
16
Find(Handle<Name> name,Handle<Map> stub_holder,Code::Kind kind,CacheHolderFlag cache_holder)17 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
18 Handle<Map> stub_holder,
19 Code::Kind kind,
20 CacheHolderFlag cache_holder) {
21 Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder);
22 Code* code = stub_holder->LookupInCodeCache(*name, flags);
23 if (code == nullptr) return Handle<Code>();
24 return handle(code);
25 }
26
27
ComputeLoadNonexistent(Handle<Name> name,Handle<Map> receiver_map)28 Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent(
29 Handle<Name> name, Handle<Map> receiver_map) {
30 Isolate* isolate = name->GetIsolate();
31 if (receiver_map->prototype()->IsNull(isolate)) {
32 // TODO(jkummerow/verwaest): If there is no prototype and the property
33 // is nonexistent, introduce a builtin to handle this (fast properties
34 // -> return undefined, dictionary properties -> do negative lookup).
35 return Handle<Code>();
36 }
37 CacheHolderFlag flag;
38 Handle<Map> stub_holder_map =
39 IC::GetHandlerCacheHolder(receiver_map, false, isolate, &flag);
40
41 // If no dictionary mode objects are present in the prototype chain, the load
42 // nonexistent IC stub can be shared for all names for a given map and we use
43 // the empty string for the map cache in that case. If there are dictionary
44 // mode objects involved, we need to do negative lookups in the stub and
45 // therefore the stub will be specific to the name.
46 Handle<Name> cache_name =
47 receiver_map->is_dictionary_map()
48 ? name
49 : Handle<Name>::cast(isolate->factory()->nonexistent_symbol());
50 Handle<Map> current_map = stub_holder_map;
51 Handle<JSObject> last(JSObject::cast(receiver_map->prototype()));
52 while (true) {
53 if (current_map->is_dictionary_map()) cache_name = name;
54 if (current_map->prototype()->IsNull(isolate)) break;
55 if (name->IsPrivate()) {
56 // TODO(verwaest): Use nonexistent_private_symbol.
57 cache_name = name;
58 if (!current_map->has_hidden_prototype()) break;
59 }
60
61 last = handle(JSObject::cast(current_map->prototype()));
62 current_map = handle(last->map());
63 }
64 // Compile the stub that is either shared for all names or
65 // name specific if there are global objects involved.
66 Handle<Code> handler = PropertyHandlerCompiler::Find(
67 cache_name, stub_holder_map, Code::LOAD_IC, flag);
68 if (!handler.is_null()) {
69 TRACE_HANDLER_STATS(isolate, LoadIC_HandlerCacheHit_NonExistent);
70 return handler;
71 }
72
73 TRACE_HANDLER_STATS(isolate, LoadIC_LoadNonexistent);
74 NamedLoadHandlerCompiler compiler(isolate, receiver_map, last, flag);
75 handler = compiler.CompileLoadNonexistent(cache_name);
76 Map::UpdateCodeCache(stub_holder_map, cache_name, handler);
77 return handler;
78 }
79
80
GetCode(Code::Kind kind,Handle<Name> name)81 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
82 Handle<Name> name) {
83 Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder());
84 Handle<Code> code = GetCodeWithFlags(flags, name);
85 PROFILE(isolate(), CodeCreateEvent(CodeEventListener::HANDLER_TAG,
86 AbstractCode::cast(*code), *name));
87 #ifdef DEBUG
88 code->VerifyEmbeddedObjects();
89 #endif
90 return code;
91 }
92
93
94 #define __ ACCESS_MASM(masm())
95
96
FrontendHeader(Register object_reg,Handle<Name> name,Label * miss,ReturnHolder return_what)97 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
98 Handle<Name> name,
99 Label* miss,
100 ReturnHolder return_what) {
101 if (map()->IsPrimitiveMap() || map()->IsJSGlobalProxyMap()) {
102 // If the receiver is a global proxy and if we get to this point then
103 // the compile-time (current) native context has access to global proxy's
104 // native context. Since access rights revocation is not supported at all,
105 // we can generate a check that an execution-time native context is either
106 // the same as compile-time native context or has the same access token.
107 Handle<Context> native_context = isolate()->native_context();
108 Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
109
110 bool compare_native_contexts_only = map()->IsPrimitiveMap();
111 GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss,
112 compare_native_contexts_only);
113 }
114
115 // Check that the maps starting from the prototype haven't changed.
116 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
117 miss, return_what);
118 }
119
120
121 // Frontend for store uses the name register. It has to be restored before a
122 // miss.
FrontendHeader(Register object_reg,Handle<Name> name,Label * miss,ReturnHolder return_what)123 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
124 Handle<Name> name,
125 Label* miss,
126 ReturnHolder return_what) {
127 if (map()->IsJSGlobalProxyMap()) {
128 Handle<Context> native_context = isolate()->native_context();
129 Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
130 GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss, false);
131 }
132
133 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
134 miss, return_what);
135 }
136
137
Frontend(Handle<Name> name)138 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
139 Label miss;
140 if (IC::ShouldPushPopSlotAndVector(kind())) {
141 PushVectorAndSlot();
142 }
143 Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER);
144 FrontendFooter(name, &miss);
145 // The footer consumes the vector and slot from the stack if miss occurs.
146 if (IC::ShouldPushPopSlotAndVector(kind())) {
147 DiscardVectorAndSlot();
148 }
149 return reg;
150 }
151
152
NonexistentFrontendHeader(Handle<Name> name,Label * miss,Register scratch1,Register scratch2)153 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
154 Label* miss,
155 Register scratch1,
156 Register scratch2) {
157 Register holder_reg;
158 Handle<Map> last_map;
159 if (holder().is_null()) {
160 holder_reg = receiver();
161 last_map = map();
162 // If |type| has null as its prototype, |holder()| is
163 // Handle<JSObject>::null().
164 DCHECK(last_map->prototype() == isolate()->heap()->null_value());
165 } else {
166 last_map = handle(holder()->map());
167 // This condition matches the branches below.
168 bool need_holder =
169 last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap();
170 holder_reg =
171 FrontendHeader(receiver(), name, miss,
172 need_holder ? RETURN_HOLDER : DONT_RETURN_ANYTHING);
173 }
174
175 if (last_map->is_dictionary_map()) {
176 if (last_map->IsJSGlobalObjectMap()) {
177 Handle<JSGlobalObject> global =
178 holder().is_null()
179 ? Handle<JSGlobalObject>::cast(isolate()->global_object())
180 : Handle<JSGlobalObject>::cast(holder());
181 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss);
182 } else {
183 if (!name->IsUniqueName()) {
184 DCHECK(name->IsString());
185 name = factory()->InternalizeString(Handle<String>::cast(name));
186 }
187 DCHECK(holder().is_null() ||
188 holder()->property_dictionary()->FindEntry(name) ==
189 NameDictionary::kNotFound);
190 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1,
191 scratch2);
192 }
193 }
194 }
195
196
CompileLoadField(Handle<Name> name,FieldIndex field)197 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name,
198 FieldIndex field) {
199 Register reg = Frontend(name);
200 __ Move(receiver(), reg);
201 LoadFieldStub stub(isolate(), field);
202 GenerateTailCall(masm(), stub.GetCode());
203 return GetCode(kind(), name);
204 }
205
206
CompileLoadConstant(Handle<Name> name,int constant_index)207 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name,
208 int constant_index) {
209 Register reg = Frontend(name);
210 __ Move(receiver(), reg);
211 LoadConstantStub stub(isolate(), constant_index);
212 GenerateTailCall(masm(), stub.GetCode());
213 return GetCode(kind(), name);
214 }
215
216
CompileLoadNonexistent(Handle<Name> name)217 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent(
218 Handle<Name> name) {
219 Label miss;
220 if (IC::ShouldPushPopSlotAndVector(kind())) {
221 DCHECK(kind() == Code::LOAD_IC);
222 PushVectorAndSlot();
223 }
224 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3());
225 if (IC::ShouldPushPopSlotAndVector(kind())) {
226 DiscardVectorAndSlot();
227 }
228 GenerateLoadConstant(isolate()->factory()->undefined_value());
229 FrontendFooter(name, &miss);
230 return GetCode(kind(), name);
231 }
232
CompileLoadCallback(Handle<Name> name,Handle<AccessorInfo> callback,Handle<Code> slow_stub)233 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
234 Handle<Name> name, Handle<AccessorInfo> callback, Handle<Code> slow_stub) {
235 if (V8_UNLIKELY(FLAG_runtime_stats)) {
236 GenerateTailCall(masm(), slow_stub);
237 }
238 Register reg = Frontend(name);
239 GenerateLoadCallback(reg, callback);
240 return GetCode(kind(), name);
241 }
242
CompileLoadCallback(Handle<Name> name,const CallOptimization & call_optimization,int accessor_index,Handle<Code> slow_stub)243 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
244 Handle<Name> name, const CallOptimization& call_optimization,
245 int accessor_index, Handle<Code> slow_stub) {
246 DCHECK(call_optimization.is_simple_api_call());
247 if (V8_UNLIKELY(FLAG_runtime_stats)) {
248 GenerateTailCall(masm(), slow_stub);
249 }
250 Register holder = Frontend(name);
251 GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(),
252 scratch2(), false, no_reg, holder, accessor_index);
253 return GetCode(kind(), name);
254 }
255
256
InterceptorVectorSlotPush(Register holder_reg)257 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
258 if (IC::ShouldPushPopSlotAndVector(kind())) {
259 if (holder_reg.is(receiver())) {
260 PushVectorAndSlot();
261 } else {
262 DCHECK(holder_reg.is(scratch1()));
263 PushVectorAndSlot(scratch2(), scratch3());
264 }
265 }
266 }
267
268
InterceptorVectorSlotPop(Register holder_reg,PopMode mode)269 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
270 PopMode mode) {
271 if (IC::ShouldPushPopSlotAndVector(kind())) {
272 if (mode == DISCARD) {
273 DiscardVectorAndSlot();
274 } else {
275 if (holder_reg.is(receiver())) {
276 PopVectorAndSlot();
277 } else {
278 DCHECK(holder_reg.is(scratch1()));
279 PopVectorAndSlot(scratch2(), scratch3());
280 }
281 }
282 }
283 }
284
285
CompileLoadInterceptor(LookupIterator * it)286 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
287 LookupIterator* it) {
288 // So far the most popular follow ups for interceptor loads are DATA and
289 // AccessorInfo, so inline only them. Other cases may be added
290 // later.
291 bool inline_followup = false;
292 switch (it->state()) {
293 case LookupIterator::TRANSITION:
294 UNREACHABLE();
295 case LookupIterator::ACCESS_CHECK:
296 case LookupIterator::INTERCEPTOR:
297 case LookupIterator::JSPROXY:
298 case LookupIterator::NOT_FOUND:
299 case LookupIterator::INTEGER_INDEXED_EXOTIC:
300 break;
301 case LookupIterator::DATA:
302 inline_followup =
303 it->property_details().type() == DATA && !it->is_dictionary_holder();
304 break;
305 case LookupIterator::ACCESSOR: {
306 Handle<Object> accessors = it->GetAccessors();
307 if (accessors->IsAccessorInfo()) {
308 Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
309 inline_followup =
310 info->getter() != NULL &&
311 AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map());
312 } else if (accessors->IsAccessorPair()) {
313 Handle<JSObject> property_holder(it->GetHolder<JSObject>());
314 Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
315 isolate());
316 if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) {
317 break;
318 }
319 if (!property_holder->HasFastProperties()) break;
320 CallOptimization call_optimization(getter);
321 Handle<Map> receiver_map = map();
322 inline_followup = call_optimization.is_simple_api_call() &&
323 call_optimization.IsCompatibleReceiverMap(
324 receiver_map, property_holder);
325 }
326 }
327 }
328
329 Label miss;
330 InterceptorVectorSlotPush(receiver());
331 bool lost_holder_register = false;
332 auto holder_orig = holder();
333 // non masking interceptors must check the entire chain, so temporarily reset
334 // the holder to be that last element for the FrontendHeader call.
335 if (holder()->GetNamedInterceptor()->non_masking()) {
336 DCHECK(!inline_followup);
337 JSObject* last = *holder();
338 PrototypeIterator iter(isolate(), last);
339 while (!iter.IsAtEnd()) {
340 lost_holder_register = true;
341 // Casting to JSObject is fine here. The LookupIterator makes sure to
342 // look behind non-masking interceptors during the original lookup, and
343 // we wouldn't try to compile a handler if there was a Proxy anywhere.
344 last = iter.GetCurrent<JSObject>();
345 iter.Advance();
346 }
347 auto last_handle = handle(last);
348 set_holder(last_handle);
349 }
350 Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER);
351 // Reset the holder so further calculations are correct.
352 set_holder(holder_orig);
353 if (lost_holder_register) {
354 if (*it->GetReceiver() == *holder()) {
355 reg = receiver();
356 } else {
357 // Reload lost holder register.
358 auto cell = isolate()->factory()->NewWeakCell(holder());
359 __ LoadWeakValue(reg, cell, &miss);
360 }
361 }
362 FrontendFooter(it->name(), &miss);
363 InterceptorVectorSlotPop(reg);
364 if (inline_followup) {
365 // TODO(368): Compile in the whole chain: all the interceptors in
366 // prototypes and ultimate answer.
367 GenerateLoadInterceptorWithFollowup(it, reg);
368 } else {
369 GenerateLoadInterceptor(reg);
370 }
371 return GetCode(kind(), it->name());
372 }
373
GenerateLoadCallback(Register reg,Handle<AccessorInfo> callback)374 void NamedLoadHandlerCompiler::GenerateLoadCallback(
375 Register reg, Handle<AccessorInfo> callback) {
376 DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister()));
377 __ Move(ApiGetterDescriptor::HolderRegister(), reg);
378 // The callback is alive if this instruction is executed,
379 // so the weak cell is not cleared and points to data.
380 Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
381 __ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell);
382
383 CallApiGetterStub stub(isolate());
384 __ TailCallStub(&stub);
385 }
386
GenerateLoadPostInterceptor(LookupIterator * it,Register interceptor_reg)387 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
388 LookupIterator* it, Register interceptor_reg) {
389 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
390
391 Handle<Map> holder_map(holder()->map());
392 set_map(holder_map);
393 set_holder(real_named_property_holder);
394
395 Label miss;
396 InterceptorVectorSlotPush(interceptor_reg);
397 Register reg =
398 FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER);
399 FrontendFooter(it->name(), &miss);
400 // We discard the vector and slot now because we don't miss below this point.
401 InterceptorVectorSlotPop(reg, DISCARD);
402
403 switch (it->state()) {
404 case LookupIterator::ACCESS_CHECK:
405 case LookupIterator::INTERCEPTOR:
406 case LookupIterator::JSPROXY:
407 case LookupIterator::NOT_FOUND:
408 case LookupIterator::INTEGER_INDEXED_EXOTIC:
409 case LookupIterator::TRANSITION:
410 UNREACHABLE();
411 case LookupIterator::DATA: {
412 DCHECK_EQ(DATA, it->property_details().type());
413 __ Move(receiver(), reg);
414 LoadFieldStub stub(isolate(), it->GetFieldIndex());
415 GenerateTailCall(masm(), stub.GetCode());
416 break;
417 }
418 case LookupIterator::ACCESSOR:
419 if (it->GetAccessors()->IsAccessorInfo()) {
420 Handle<AccessorInfo> info =
421 Handle<AccessorInfo>::cast(it->GetAccessors());
422 DCHECK_NOT_NULL(info->getter());
423 GenerateLoadCallback(reg, info);
424 } else {
425 Handle<Object> function = handle(
426 AccessorPair::cast(*it->GetAccessors())->getter(), isolate());
427 CallOptimization call_optimization(function);
428 GenerateApiAccessorCall(masm(), call_optimization, holder_map,
429 receiver(), scratch2(), false, no_reg, reg,
430 it->GetAccessorIndex());
431 }
432 }
433 }
434
CompileLoadViaGetter(Handle<Name> name,int accessor_index,int expected_arguments)435 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
436 Handle<Name> name, int accessor_index, int expected_arguments) {
437 Register holder = Frontend(name);
438 GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index,
439 expected_arguments, scratch2());
440 return GetCode(kind(), name);
441 }
442
443
444 // TODO(verwaest): Cleanup. holder() is actually the receiver.
CompileStoreTransition(Handle<Map> transition,Handle<Name> name)445 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
446 Handle<Map> transition, Handle<Name> name) {
447 Label miss;
448
449 // Ensure that the StoreTransitionStub we are going to call has the same
450 // number of stack arguments. This means that we don't have to adapt them
451 // if we decide to call the transition or miss stub.
452 STATIC_ASSERT(Descriptor::kStackArgumentsCount ==
453 StoreTransitionDescriptor::kStackArgumentsCount);
454 STATIC_ASSERT(Descriptor::kStackArgumentsCount == 0 ||
455 Descriptor::kStackArgumentsCount == 3);
456 STATIC_ASSERT(Descriptor::kParameterCount - Descriptor::kValue ==
457 StoreTransitionDescriptor::kParameterCount -
458 StoreTransitionDescriptor::kValue);
459 STATIC_ASSERT(Descriptor::kParameterCount - Descriptor::kSlot ==
460 StoreTransitionDescriptor::kParameterCount -
461 StoreTransitionDescriptor::kSlot);
462 STATIC_ASSERT(Descriptor::kParameterCount - Descriptor::kVector ==
463 StoreTransitionDescriptor::kParameterCount -
464 StoreTransitionDescriptor::kVector);
465
466 if (Descriptor::kPassLastArgsOnStack) {
467 __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue);
468 }
469
470 bool need_save_restore = IC::ShouldPushPopSlotAndVector(kind());
471 if (need_save_restore) {
472 PushVectorAndSlot();
473 }
474
475 // Check that we are allowed to write this.
476 bool is_nonexistent = holder()->map() == transition->GetBackPointer();
477 if (is_nonexistent) {
478 // Find the top object.
479 Handle<JSObject> last;
480 PrototypeIterator::WhereToEnd end =
481 name->IsPrivate() ? PrototypeIterator::END_AT_NON_HIDDEN
482 : PrototypeIterator::END_AT_NULL;
483 PrototypeIterator iter(isolate(), holder(), kStartAtPrototype, end);
484 while (!iter.IsAtEnd()) {
485 last = PrototypeIterator::GetCurrent<JSObject>(iter);
486 iter.Advance();
487 }
488 if (!last.is_null()) set_holder(last);
489 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
490 } else {
491 FrontendHeader(receiver(), name, &miss, DONT_RETURN_ANYTHING);
492 DCHECK(holder()->HasFastProperties());
493 }
494
495 int descriptor = transition->LastAdded();
496 Handle<DescriptorArray> descriptors(transition->instance_descriptors());
497 PropertyDetails details = descriptors->GetDetails(descriptor);
498 Representation representation = details.representation();
499 DCHECK(!representation.IsNone());
500
501 // Stub is never generated for objects that require access checks.
502 DCHECK(!transition->is_access_check_needed());
503
504 // Call to respective StoreTransitionStub.
505 Register map_reg = StoreTransitionDescriptor::MapRegister();
506
507 if (details.type() == DATA_CONSTANT) {
508 DCHECK(descriptors->GetValue(descriptor)->IsJSFunction());
509 GenerateRestoreMap(transition, map_reg, scratch1(), &miss);
510 GenerateConstantCheck(map_reg, descriptor, value(), scratch1(), &miss);
511 if (need_save_restore) {
512 PopVectorAndSlot();
513 }
514 GenerateRestoreName(name);
515 StoreMapStub stub(isolate());
516 GenerateTailCall(masm(), stub.GetCode());
517
518 } else {
519 if (representation.IsHeapObject()) {
520 GenerateFieldTypeChecks(descriptors->GetFieldType(descriptor), value(),
521 &miss);
522 }
523 StoreTransitionStub::StoreMode store_mode =
524 Map::cast(transition->GetBackPointer())->unused_property_fields() == 0
525 ? StoreTransitionStub::ExtendStorageAndStoreMapAndValue
526 : StoreTransitionStub::StoreMapAndValue;
527 GenerateRestoreMap(transition, map_reg, scratch1(), &miss);
528 if (need_save_restore) {
529 PopVectorAndSlot();
530 }
531 // We need to pass name on the stack.
532 PopReturnAddress(this->name());
533 __ Push(name);
534 PushReturnAddress(this->name());
535
536 FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor);
537 __ Move(StoreNamedTransitionDescriptor::FieldOffsetRegister(),
538 Smi::FromInt(index.index() << kPointerSizeLog2));
539
540 StoreTransitionStub stub(isolate(), index.is_inobject(), representation,
541 store_mode);
542 GenerateTailCall(masm(), stub.GetCode());
543 }
544
545 __ bind(&miss);
546 if (need_save_restore) {
547 PopVectorAndSlot();
548 }
549 GenerateRestoreName(name);
550 TailCallBuiltin(masm(), MissBuiltin(kind()));
551
552 return GetCode(kind(), name);
553 }
554
RequiresFieldTypeChecks(FieldType * field_type) const555 bool NamedStoreHandlerCompiler::RequiresFieldTypeChecks(
556 FieldType* field_type) const {
557 return field_type->IsClass();
558 }
559
560
CompileStoreField(LookupIterator * it)561 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) {
562 Label miss;
563 DCHECK(it->representation().IsHeapObject());
564
565 FieldType* field_type = *it->GetFieldType();
566 bool need_save_restore = false;
567 if (RequiresFieldTypeChecks(field_type)) {
568 need_save_restore = IC::ShouldPushPopSlotAndVector(kind());
569 if (Descriptor::kPassLastArgsOnStack) {
570 __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue);
571 }
572 if (need_save_restore) PushVectorAndSlot();
573 GenerateFieldTypeChecks(field_type, value(), &miss);
574 if (need_save_restore) PopVectorAndSlot();
575 }
576
577 StoreFieldStub stub(isolate(), it->GetFieldIndex(), it->representation());
578 GenerateTailCall(masm(), stub.GetCode());
579
580 __ bind(&miss);
581 if (need_save_restore) PopVectorAndSlot();
582 TailCallBuiltin(masm(), MissBuiltin(kind()));
583 return GetCode(kind(), it->name());
584 }
585
586
CompileStoreViaSetter(Handle<JSObject> object,Handle<Name> name,int accessor_index,int expected_arguments)587 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
588 Handle<JSObject> object, Handle<Name> name, int accessor_index,
589 int expected_arguments) {
590 Register holder = Frontend(name);
591 GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index,
592 expected_arguments, scratch2());
593
594 return GetCode(kind(), name);
595 }
596
CompileStoreCallback(Handle<JSObject> object,Handle<Name> name,const CallOptimization & call_optimization,int accessor_index,Handle<Code> slow_stub)597 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
598 Handle<JSObject> object, Handle<Name> name,
599 const CallOptimization& call_optimization, int accessor_index,
600 Handle<Code> slow_stub) {
601 if (V8_UNLIKELY(FLAG_runtime_stats)) {
602 GenerateTailCall(masm(), slow_stub);
603 }
604 Register holder = Frontend(name);
605 if (Descriptor::kPassLastArgsOnStack) {
606 __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue);
607 }
608 GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()),
609 receiver(), scratch2(), true, value(), holder,
610 accessor_index);
611 return GetCode(kind(), name);
612 }
613
614
615 #undef __
616
617 // static
GetKeyedLoadHandler(Handle<Map> receiver_map,Isolate * isolate)618 Handle<Object> ElementHandlerCompiler::GetKeyedLoadHandler(
619 Handle<Map> receiver_map, Isolate* isolate) {
620 if (receiver_map->has_indexed_interceptor() &&
621 !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(isolate) &&
622 !receiver_map->GetIndexedInterceptor()->non_masking()) {
623 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedInterceptorStub);
624 return LoadIndexedInterceptorStub(isolate).GetCode();
625 }
626 if (receiver_map->IsStringMap()) {
627 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedStringStub);
628 return LoadIndexedStringStub(isolate).GetCode();
629 }
630 InstanceType instance_type = receiver_map->instance_type();
631 if (instance_type < FIRST_JS_RECEIVER_TYPE) {
632 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_SlowStub);
633 return isolate->builtins()->KeyedLoadIC_Slow();
634 }
635
636 ElementsKind elements_kind = receiver_map->elements_kind();
637 if (IsSloppyArgumentsElements(elements_kind)) {
638 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_KeyedLoadSloppyArgumentsStub);
639 return KeyedLoadSloppyArgumentsStub(isolate).GetCode();
640 }
641 bool is_js_array = instance_type == JS_ARRAY_TYPE;
642 if (elements_kind == DICTIONARY_ELEMENTS) {
643 if (FLAG_tf_load_ic_stub) {
644 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
645 return LoadHandler::LoadElement(isolate, elements_kind, false,
646 is_js_array);
647 }
648 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadDictionaryElementStub);
649 return LoadDictionaryElementStub(isolate).GetCode();
650 }
651 DCHECK(IsFastElementsKind(elements_kind) ||
652 IsFixedTypedArrayElementsKind(elements_kind));
653 // TODO(jkummerow): Use IsHoleyElementsKind(elements_kind).
654 bool convert_hole_to_undefined =
655 is_js_array && elements_kind == FAST_HOLEY_ELEMENTS &&
656 *receiver_map == isolate->get_initial_js_array_map(elements_kind);
657 if (FLAG_tf_load_ic_stub) {
658 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
659 return LoadHandler::LoadElement(isolate, elements_kind,
660 convert_hole_to_undefined, is_js_array);
661 } else {
662 TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadFastElementStub);
663 return LoadFastElementStub(isolate, is_js_array, elements_kind,
664 convert_hole_to_undefined)
665 .GetCode();
666 }
667 }
668
CompileElementHandlers(MapHandleList * receiver_maps,List<Handle<Object>> * handlers)669 void ElementHandlerCompiler::CompileElementHandlers(
670 MapHandleList* receiver_maps, List<Handle<Object>>* handlers) {
671 for (int i = 0; i < receiver_maps->length(); ++i) {
672 handlers->Add(GetKeyedLoadHandler(receiver_maps->at(i), isolate()));
673 }
674 }
675 } // namespace internal
676 } // namespace v8
677