• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetCode(Code::Kind kind,Handle<Name> name)27 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
28                                               Handle<Name> name) {
29   Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder());
30   Handle<Code> code = GetCodeWithFlags(flags, name);
31   PROFILE(isolate(), CodeCreateEvent(CodeEventListener::HANDLER_TAG,
32                                      AbstractCode::cast(*code), *name));
33 #ifdef DEBUG
34   code->VerifyEmbeddedObjects();
35 #endif
36   return code;
37 }
38 
39 
40 #define __ ACCESS_MASM(masm())
41 
42 
FrontendHeader(Register object_reg,Handle<Name> name,Label * miss,ReturnHolder return_what)43 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
44                                                   Handle<Name> name,
45                                                   Label* miss,
46                                                   ReturnHolder return_what) {
47   if (map()->IsPrimitiveMap() || map()->IsJSGlobalProxyMap()) {
48     // If the receiver is a global proxy and if we get to this point then
49     // the compile-time (current) native context has access to global proxy's
50     // native context. Since access rights revocation is not supported at all,
51     // we can generate a check that an execution-time native context is either
52     // the same as compile-time native context or has the same access token.
53     Handle<Context> native_context = isolate()->native_context();
54     Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
55 
56     bool compare_native_contexts_only = map()->IsPrimitiveMap();
57     GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss,
58                         compare_native_contexts_only);
59   }
60 
61   // Check that the maps starting from the prototype haven't changed.
62   return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
63                          miss, return_what);
64 }
65 
66 
67 // Frontend for store uses the name register. It has to be restored before a
68 // miss.
FrontendHeader(Register object_reg,Handle<Name> name,Label * miss,ReturnHolder return_what)69 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
70                                                    Handle<Name> name,
71                                                    Label* miss,
72                                                    ReturnHolder return_what) {
73   if (map()->IsJSGlobalProxyMap()) {
74     Handle<Context> native_context = isolate()->native_context();
75     Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate());
76     GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss, false);
77   }
78 
79   return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
80                          miss, return_what);
81 }
82 
83 
Frontend(Handle<Name> name)84 Register PropertyHandlerCompiler::Frontend(Handle<Name> name) {
85   Label miss;
86   if (IC::ShouldPushPopSlotAndVector(kind())) {
87     PushVectorAndSlot();
88   }
89   Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER);
90   FrontendFooter(name, &miss);
91   // The footer consumes the vector and slot from the stack if miss occurs.
92   if (IC::ShouldPushPopSlotAndVector(kind())) {
93     DiscardVectorAndSlot();
94   }
95   return reg;
96 }
97 
CompileLoadCallback(Handle<Name> name,Handle<AccessorInfo> callback,Handle<Code> slow_stub)98 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
99     Handle<Name> name, Handle<AccessorInfo> callback, Handle<Code> slow_stub) {
100   if (V8_UNLIKELY(FLAG_runtime_stats)) {
101     GenerateTailCall(masm(), slow_stub);
102   }
103   Register reg = Frontend(name);
104   GenerateLoadCallback(reg, callback);
105   return GetCode(kind(), name);
106 }
107 
CompileLoadCallback(Handle<Name> name,const CallOptimization & call_optimization,int accessor_index,Handle<Code> slow_stub)108 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
109     Handle<Name> name, const CallOptimization& call_optimization,
110     int accessor_index, Handle<Code> slow_stub) {
111   DCHECK(call_optimization.is_simple_api_call());
112   if (V8_UNLIKELY(FLAG_runtime_stats)) {
113     GenerateTailCall(masm(), slow_stub);
114   }
115   Register holder = Frontend(name);
116   GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(),
117                           scratch2(), false, no_reg, holder, accessor_index);
118   return GetCode(kind(), name);
119 }
120 
121 
InterceptorVectorSlotPush(Register holder_reg)122 void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) {
123   if (IC::ShouldPushPopSlotAndVector(kind())) {
124     if (holder_reg.is(receiver())) {
125       PushVectorAndSlot();
126     } else {
127       DCHECK(holder_reg.is(scratch1()));
128       PushVectorAndSlot(scratch2(), scratch3());
129     }
130   }
131 }
132 
133 
InterceptorVectorSlotPop(Register holder_reg,PopMode mode)134 void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg,
135                                                         PopMode mode) {
136   if (IC::ShouldPushPopSlotAndVector(kind())) {
137     if (mode == DISCARD) {
138       DiscardVectorAndSlot();
139     } else {
140       if (holder_reg.is(receiver())) {
141         PopVectorAndSlot();
142       } else {
143         DCHECK(holder_reg.is(scratch1()));
144         PopVectorAndSlot(scratch2(), scratch3());
145       }
146     }
147   }
148 }
149 
150 
CompileLoadInterceptor(LookupIterator * it)151 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
152     LookupIterator* it) {
153   // So far the most popular follow ups for interceptor loads are DATA and
154   // AccessorInfo, so inline only them. Other cases may be added
155   // later.
156   bool inline_followup = false;
157   switch (it->state()) {
158     case LookupIterator::TRANSITION:
159       UNREACHABLE();
160     case LookupIterator::ACCESS_CHECK:
161     case LookupIterator::INTERCEPTOR:
162     case LookupIterator::JSPROXY:
163     case LookupIterator::NOT_FOUND:
164     case LookupIterator::INTEGER_INDEXED_EXOTIC:
165       break;
166     case LookupIterator::DATA: {
167       PropertyDetails details = it->property_details();
168       inline_followup = details.kind() == kData &&
169                         details.location() == kField &&
170                         !it->is_dictionary_holder();
171       break;
172     }
173     case LookupIterator::ACCESSOR: {
174       Handle<Object> accessors = it->GetAccessors();
175       if (accessors->IsAccessorInfo()) {
176         Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors);
177         inline_followup =
178             info->getter() != NULL &&
179             AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map());
180       } else if (accessors->IsAccessorPair()) {
181         Handle<JSObject> property_holder(it->GetHolder<JSObject>());
182         Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
183                               isolate());
184         if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) {
185           break;
186         }
187         if (!property_holder->HasFastProperties()) break;
188         CallOptimization call_optimization(getter);
189         Handle<Map> receiver_map = map();
190         inline_followup = call_optimization.is_simple_api_call() &&
191                           call_optimization.IsCompatibleReceiverMap(
192                               receiver_map, property_holder);
193       }
194     }
195   }
196 
197   Label miss;
198   InterceptorVectorSlotPush(receiver());
199   bool lost_holder_register = false;
200   auto holder_orig = holder();
201   // non masking interceptors must check the entire chain, so temporarily reset
202   // the holder to be that last element for the FrontendHeader call.
203   if (holder()->GetNamedInterceptor()->non_masking()) {
204     DCHECK(!inline_followup);
205     JSObject* last = *holder();
206     PrototypeIterator iter(isolate(), last);
207     while (!iter.IsAtEnd()) {
208       lost_holder_register = true;
209       // Casting to JSObject is fine here. The LookupIterator makes sure to
210       // look behind non-masking interceptors during the original lookup, and
211       // we wouldn't try to compile a handler if there was a Proxy anywhere.
212       last = iter.GetCurrent<JSObject>();
213       iter.Advance();
214     }
215     auto last_handle = handle(last);
216     set_holder(last_handle);
217   }
218   Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER);
219   // Reset the holder so further calculations are correct.
220   set_holder(holder_orig);
221   if (lost_holder_register) {
222     if (*it->GetReceiver() == *holder()) {
223       reg = receiver();
224     } else {
225       // Reload lost holder register.
226       auto cell = isolate()->factory()->NewWeakCell(holder());
227       __ LoadWeakValue(reg, cell, &miss);
228     }
229   }
230   FrontendFooter(it->name(), &miss);
231   InterceptorVectorSlotPop(reg);
232   if (inline_followup) {
233     // TODO(368): Compile in the whole chain: all the interceptors in
234     // prototypes and ultimate answer.
235     GenerateLoadInterceptorWithFollowup(it, reg);
236   } else {
237     GenerateLoadInterceptor(reg);
238   }
239   return GetCode(kind(), it->name());
240 }
241 
GenerateLoadCallback(Register reg,Handle<AccessorInfo> callback)242 void NamedLoadHandlerCompiler::GenerateLoadCallback(
243     Register reg, Handle<AccessorInfo> callback) {
244   DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister()));
245   __ Move(ApiGetterDescriptor::HolderRegister(), reg);
246   // The callback is alive if this instruction is executed,
247   // so the weak cell is not cleared and points to data.
248   Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback);
249   __ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell);
250 
251   CallApiGetterStub stub(isolate());
252   __ TailCallStub(&stub);
253 }
254 
GenerateLoadPostInterceptor(LookupIterator * it,Register interceptor_reg)255 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
256     LookupIterator* it, Register interceptor_reg) {
257   Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
258 
259   Handle<Map> holder_map(holder()->map());
260   set_map(holder_map);
261   set_holder(real_named_property_holder);
262 
263   Label miss;
264   InterceptorVectorSlotPush(interceptor_reg);
265   Register reg =
266       FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER);
267   FrontendFooter(it->name(), &miss);
268   // We discard the vector and slot now because we don't miss below this point.
269   InterceptorVectorSlotPop(reg, DISCARD);
270 
271   switch (it->state()) {
272     case LookupIterator::ACCESS_CHECK:
273     case LookupIterator::INTERCEPTOR:
274     case LookupIterator::JSPROXY:
275     case LookupIterator::NOT_FOUND:
276     case LookupIterator::INTEGER_INDEXED_EXOTIC:
277     case LookupIterator::TRANSITION:
278       UNREACHABLE();
279     case LookupIterator::DATA: {
280       DCHECK_EQ(kData, it->property_details().kind());
281       DCHECK_EQ(kField, it->property_details().location());
282       __ Move(LoadFieldDescriptor::ReceiverRegister(), reg);
283       Handle<Object> smi_handler =
284           LoadIC::SimpleFieldLoad(isolate(), it->GetFieldIndex());
285       __ Move(LoadFieldDescriptor::SmiHandlerRegister(), smi_handler);
286       GenerateTailCall(masm(), isolate()->builtins()->LoadField());
287       break;
288     }
289     case LookupIterator::ACCESSOR:
290       if (it->GetAccessors()->IsAccessorInfo()) {
291         Handle<AccessorInfo> info =
292             Handle<AccessorInfo>::cast(it->GetAccessors());
293         DCHECK_NOT_NULL(info->getter());
294         GenerateLoadCallback(reg, info);
295       } else {
296         Handle<Object> function = handle(
297             AccessorPair::cast(*it->GetAccessors())->getter(), isolate());
298         CallOptimization call_optimization(function);
299         GenerateApiAccessorCall(masm(), call_optimization, holder_map,
300                                 receiver(), scratch2(), false, no_reg, reg,
301                                 it->GetAccessorIndex());
302       }
303   }
304 }
305 
CompileLoadViaGetter(Handle<Name> name,int accessor_index,int expected_arguments)306 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
307     Handle<Name> name, int accessor_index, int expected_arguments) {
308   Register holder = Frontend(name);
309   GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index,
310                         expected_arguments, scratch2());
311   return GetCode(kind(), name);
312 }
313 
CompileStoreViaSetter(Handle<JSObject> object,Handle<Name> name,int accessor_index,int expected_arguments)314 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
315     Handle<JSObject> object, Handle<Name> name, int accessor_index,
316     int expected_arguments) {
317   Register holder = Frontend(name);
318   GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index,
319                          expected_arguments, scratch2());
320 
321   return GetCode(kind(), name);
322 }
323 
CompileStoreCallback(Handle<JSObject> object,Handle<Name> name,const CallOptimization & call_optimization,int accessor_index,Handle<Code> slow_stub)324 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
325     Handle<JSObject> object, Handle<Name> name,
326     const CallOptimization& call_optimization, int accessor_index,
327     Handle<Code> slow_stub) {
328   if (V8_UNLIKELY(FLAG_runtime_stats)) {
329     GenerateTailCall(masm(), slow_stub);
330   }
331   Register holder = Frontend(name);
332   if (Descriptor::kPassLastArgsOnStack) {
333     __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue);
334   }
335   GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()),
336                           receiver(), scratch2(), true, value(), holder,
337                           accessor_index);
338   return GetCode(kind(), name);
339 }
340 
341 
342 #undef __
343 
344 // static
GetKeyedLoadHandler(Handle<Map> receiver_map,Isolate * isolate)345 Handle<Object> ElementHandlerCompiler::GetKeyedLoadHandler(
346     Handle<Map> receiver_map, Isolate* isolate) {
347   if (receiver_map->has_indexed_interceptor() &&
348       !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(isolate) &&
349       !receiver_map->GetIndexedInterceptor()->non_masking()) {
350     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedInterceptorStub);
351     return LoadIndexedInterceptorStub(isolate).GetCode();
352   }
353   if (receiver_map->IsStringMap()) {
354     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedStringStub);
355     return isolate->builtins()->KeyedLoadIC_IndexedString();
356   }
357   InstanceType instance_type = receiver_map->instance_type();
358   if (instance_type < FIRST_JS_RECEIVER_TYPE) {
359     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_SlowStub);
360     return isolate->builtins()->KeyedLoadIC_Slow();
361   }
362 
363   ElementsKind elements_kind = receiver_map->elements_kind();
364   if (IsSloppyArgumentsElements(elements_kind)) {
365     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_KeyedLoadSloppyArgumentsStub);
366     return KeyedLoadSloppyArgumentsStub(isolate).GetCode();
367   }
368   bool is_js_array = instance_type == JS_ARRAY_TYPE;
369   if (elements_kind == DICTIONARY_ELEMENTS) {
370     TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
371     return LoadHandler::LoadElement(isolate, elements_kind, false, is_js_array);
372   }
373   DCHECK(IsFastElementsKind(elements_kind) ||
374          IsFixedTypedArrayElementsKind(elements_kind));
375   // TODO(jkummerow): Use IsHoleyElementsKind(elements_kind).
376   bool convert_hole_to_undefined =
377       is_js_array && elements_kind == FAST_HOLEY_ELEMENTS &&
378       *receiver_map == isolate->get_initial_js_array_map(elements_kind);
379   TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH);
380   return LoadHandler::LoadElement(isolate, elements_kind,
381                                   convert_hole_to_undefined, is_js_array);
382 }
383 
CompileElementHandlers(MapHandleList * receiver_maps,List<Handle<Object>> * handlers)384 void ElementHandlerCompiler::CompileElementHandlers(
385     MapHandleList* receiver_maps, List<Handle<Object>>* handlers) {
386   for (int i = 0; i < receiver_maps->length(); ++i) {
387     handlers->Add(GetKeyedLoadHandler(receiver_maps->at(i), isolate()));
388   }
389 }
390 }  // namespace internal
391 }  // namespace v8
392