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
14namespace v8 {
15namespace internal {
16
17Handle<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
27Handle<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
43Register 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.
69Register 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
84Register 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
98Handle<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
108Handle<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
122void 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
134void 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
151Handle<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
242void 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
255void 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
306Handle<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
314Handle<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
324Handle<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
345Handle<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
384void 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