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/v8.h"
6
7#include "src/ic/call-optimization.h"
8#include "src/ic/handler-compiler.h"
9#include "src/ic/ic.h"
10#include "src/ic/ic-inl.h"
11
12namespace v8 {
13namespace internal {
14
15
16Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name,
17                                           Handle<Map> stub_holder,
18                                           Code::Kind kind,
19                                           CacheHolderFlag cache_holder,
20                                           Code::StubType type) {
21  Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder);
22  Object* probe = stub_holder->FindInCodeCache(*name, flags);
23  if (probe->IsCode()) return handle(Code::cast(probe));
24  return Handle<Code>::null();
25}
26
27
28Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent(
29    Handle<Name> name, Handle<HeapType> type) {
30  Isolate* isolate = name->GetIsolate();
31  Handle<Map> receiver_map = IC::TypeToMap(*type, isolate);
32  if (receiver_map->prototype()->IsNull()) {
33    // TODO(jkummerow/verwaest): If there is no prototype and the property
34    // is nonexistent, introduce a builtin to handle this (fast properties
35    // -> return undefined, dictionary properties -> do negative lookup).
36    return Handle<Code>();
37  }
38  CacheHolderFlag flag;
39  Handle<Map> stub_holder_map =
40      IC::GetHandlerCacheHolder(*type, false, isolate, &flag);
41
42  // If no dictionary mode objects are present in the prototype chain, the load
43  // nonexistent IC stub can be shared for all names for a given map and we use
44  // the empty string for the map cache in that case. If there are dictionary
45  // mode objects involved, we need to do negative lookups in the stub and
46  // therefore the stub will be specific to the name.
47  Handle<Name> cache_name =
48      receiver_map->is_dictionary_map()
49          ? name
50          : Handle<Name>::cast(isolate->factory()->nonexistent_symbol());
51  Handle<Map> current_map = stub_holder_map;
52  Handle<JSObject> last(JSObject::cast(receiver_map->prototype()));
53  while (true) {
54    if (current_map->is_dictionary_map()) cache_name = name;
55    if (current_map->prototype()->IsNull()) break;
56    last = handle(JSObject::cast(current_map->prototype()));
57    current_map = handle(last->map());
58  }
59  // Compile the stub that is either shared for all names or
60  // name specific if there are global objects involved.
61  Handle<Code> handler = PropertyHandlerCompiler::Find(
62      cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST);
63  if (!handler.is_null()) return handler;
64
65  NamedLoadHandlerCompiler compiler(isolate, type, last, flag);
66  handler = compiler.CompileLoadNonexistent(cache_name);
67  Map::UpdateCodeCache(stub_holder_map, cache_name, handler);
68  return handler;
69}
70
71
72Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind,
73                                              Code::StubType type,
74                                              Handle<Name> name) {
75  Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder());
76  Handle<Code> code = GetCodeWithFlags(flags, name);
77  PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, *name));
78  return code;
79}
80
81
82void PropertyHandlerCompiler::set_type_for_object(Handle<Object> object) {
83  type_ = IC::CurrentTypeOf(object, isolate());
84}
85
86
87#define __ ACCESS_MASM(masm())
88
89
90Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg,
91                                                  Handle<Name> name,
92                                                  Label* miss) {
93  PrototypeCheckType check_type = CHECK_ALL_MAPS;
94  int function_index = -1;
95  if (type()->Is(HeapType::String())) {
96    function_index = Context::STRING_FUNCTION_INDEX;
97  } else if (type()->Is(HeapType::Symbol())) {
98    function_index = Context::SYMBOL_FUNCTION_INDEX;
99  } else if (type()->Is(HeapType::Number())) {
100    function_index = Context::NUMBER_FUNCTION_INDEX;
101  } else if (type()->Is(HeapType::Boolean())) {
102    function_index = Context::BOOLEAN_FUNCTION_INDEX;
103  } else {
104    check_type = SKIP_RECEIVER;
105  }
106
107  if (check_type == CHECK_ALL_MAPS) {
108    GenerateDirectLoadGlobalFunctionPrototype(masm(), function_index,
109                                              scratch1(), miss);
110    Object* function = isolate()->native_context()->get(function_index);
111    Object* prototype = JSFunction::cast(function)->instance_prototype();
112    set_type_for_object(handle(prototype, isolate()));
113    object_reg = scratch1();
114  }
115
116  // Check that the maps starting from the prototype haven't changed.
117  return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name,
118                         miss, check_type);
119}
120
121
122// Frontend for store uses the name register. It has to be restored before a
123// miss.
124Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg,
125                                                   Handle<Name> name,
126                                                   Label* miss) {
127  return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name,
128                         miss, SKIP_RECEIVER);
129}
130
131
132Register PropertyHandlerCompiler::Frontend(Register object_reg,
133                                           Handle<Name> name) {
134  Label miss;
135  Register reg = FrontendHeader(object_reg, name, &miss);
136  FrontendFooter(name, &miss);
137  return reg;
138}
139
140
141void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name,
142                                                        Label* miss,
143                                                        Register scratch1,
144                                                        Register scratch2) {
145  Register holder_reg;
146  Handle<Map> last_map;
147  if (holder().is_null()) {
148    holder_reg = receiver();
149    last_map = IC::TypeToMap(*type(), isolate());
150    // If |type| has null as its prototype, |holder()| is
151    // Handle<JSObject>::null().
152    DCHECK(last_map->prototype() == isolate()->heap()->null_value());
153  } else {
154    holder_reg = FrontendHeader(receiver(), name, miss);
155    last_map = handle(holder()->map());
156  }
157
158  if (last_map->is_dictionary_map()) {
159    if (last_map->IsJSGlobalObjectMap()) {
160      Handle<JSGlobalObject> global =
161          holder().is_null()
162              ? Handle<JSGlobalObject>::cast(type()->AsConstant()->Value())
163              : Handle<JSGlobalObject>::cast(holder());
164      GenerateCheckPropertyCell(masm(), global, name, scratch1, miss);
165    } else {
166      if (!name->IsUniqueName()) {
167        DCHECK(name->IsString());
168        name = factory()->InternalizeString(Handle<String>::cast(name));
169      }
170      DCHECK(holder().is_null() ||
171             holder()->property_dictionary()->FindEntry(name) ==
172                 NameDictionary::kNotFound);
173      GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1,
174                                       scratch2);
175    }
176  }
177}
178
179
180Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name,
181                                                        FieldIndex field) {
182  Register reg = Frontend(receiver(), name);
183  __ Move(receiver(), reg);
184  LoadFieldStub stub(isolate(), field);
185  GenerateTailCall(masm(), stub.GetCode());
186  return GetCode(kind(), Code::FAST, name);
187}
188
189
190Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name,
191                                                           int constant_index) {
192  Register reg = Frontend(receiver(), name);
193  __ Move(receiver(), reg);
194  LoadConstantStub stub(isolate(), constant_index);
195  GenerateTailCall(masm(), stub.GetCode());
196  return GetCode(kind(), Code::FAST, name);
197}
198
199
200Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent(
201    Handle<Name> name) {
202  Label miss;
203  NonexistentFrontendHeader(name, &miss, scratch2(), scratch3());
204  GenerateLoadConstant(isolate()->factory()->undefined_value());
205  FrontendFooter(name, &miss);
206  return GetCode(kind(), Code::FAST, name);
207}
208
209
210Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
211    Handle<Name> name, Handle<ExecutableAccessorInfo> callback) {
212  Register reg = Frontend(receiver(), name);
213  GenerateLoadCallback(reg, callback);
214  return GetCode(kind(), Code::FAST, name);
215}
216
217
218Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback(
219    Handle<Name> name, const CallOptimization& call_optimization) {
220  DCHECK(call_optimization.is_simple_api_call());
221  Frontend(receiver(), name);
222  Handle<Map> receiver_map = IC::TypeToMap(*type(), isolate());
223  GenerateFastApiCall(masm(), call_optimization, receiver_map, receiver(),
224                      scratch1(), false, 0, NULL);
225  return GetCode(kind(), Code::FAST, name);
226}
227
228
229Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor(
230    LookupIterator* it) {
231  // So far the most popular follow ups for interceptor loads are FIELD and
232  // ExecutableAccessorInfo, so inline only them. Other cases may be added
233  // later.
234  bool inline_followup = false;
235  switch (it->state()) {
236    case LookupIterator::TRANSITION:
237      UNREACHABLE();
238    case LookupIterator::ACCESS_CHECK:
239    case LookupIterator::INTERCEPTOR:
240    case LookupIterator::JSPROXY:
241    case LookupIterator::NOT_FOUND:
242      break;
243    case LookupIterator::DATA:
244      inline_followup = it->property_details().type() == FIELD;
245      break;
246    case LookupIterator::ACCESSOR: {
247      Handle<Object> accessors = it->GetAccessors();
248      inline_followup = accessors->IsExecutableAccessorInfo();
249      if (!inline_followup) break;
250      Handle<ExecutableAccessorInfo> info =
251          Handle<ExecutableAccessorInfo>::cast(accessors);
252      inline_followup = info->getter() != NULL &&
253                        ExecutableAccessorInfo::IsCompatibleReceiverType(
254                            isolate(), info, type());
255    }
256  }
257
258  Register reg = Frontend(receiver(), it->name());
259  if (inline_followup) {
260    // TODO(368): Compile in the whole chain: all the interceptors in
261    // prototypes and ultimate answer.
262    GenerateLoadInterceptorWithFollowup(it, reg);
263  } else {
264    GenerateLoadInterceptor(reg);
265  }
266  return GetCode(kind(), Code::FAST, it->name());
267}
268
269
270void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor(
271    LookupIterator* it, Register interceptor_reg) {
272  Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>());
273
274  set_type_for_object(holder());
275  set_holder(real_named_property_holder);
276  Register reg = Frontend(interceptor_reg, it->name());
277
278  switch (it->state()) {
279    case LookupIterator::ACCESS_CHECK:
280    case LookupIterator::INTERCEPTOR:
281    case LookupIterator::JSPROXY:
282    case LookupIterator::NOT_FOUND:
283    case LookupIterator::TRANSITION:
284      UNREACHABLE();
285    case LookupIterator::DATA: {
286      DCHECK_EQ(FIELD, it->property_details().type());
287      __ Move(receiver(), reg);
288      LoadFieldStub stub(isolate(), it->GetFieldIndex());
289      GenerateTailCall(masm(), stub.GetCode());
290      break;
291    }
292    case LookupIterator::ACCESSOR:
293      Handle<ExecutableAccessorInfo> info =
294          Handle<ExecutableAccessorInfo>::cast(it->GetAccessors());
295      DCHECK_NE(NULL, info->getter());
296      GenerateLoadCallback(reg, info);
297  }
298}
299
300
301Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter(
302    Handle<Name> name, Handle<JSFunction> getter) {
303  Frontend(receiver(), name);
304  GenerateLoadViaGetter(masm(), type(), receiver(), getter);
305  return GetCode(kind(), Code::FAST, name);
306}
307
308
309// TODO(verwaest): Cleanup. holder() is actually the receiver.
310Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition(
311    Handle<Map> transition, Handle<Name> name) {
312  Label miss, slow;
313
314  // Ensure no transitions to deprecated maps are followed.
315  __ CheckMapDeprecated(transition, scratch1(), &miss);
316
317  // Check that we are allowed to write this.
318  bool is_nonexistent = holder()->map() == transition->GetBackPointer();
319  if (is_nonexistent) {
320    // Find the top object.
321    Handle<JSObject> last;
322    PrototypeIterator iter(isolate(), holder());
323    while (!iter.IsAtEnd()) {
324      last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
325      iter.Advance();
326    }
327    if (!last.is_null()) set_holder(last);
328    NonexistentFrontendHeader(name, &miss, scratch1(), scratch2());
329  } else {
330    FrontendHeader(receiver(), name, &miss);
331    DCHECK(holder()->HasFastProperties());
332  }
333
334  GenerateStoreTransition(transition, name, receiver(), this->name(), value(),
335                          scratch1(), scratch2(), scratch3(), &miss, &slow);
336
337  GenerateRestoreName(&miss, name);
338  TailCallBuiltin(masm(), MissBuiltin(kind()));
339
340  GenerateRestoreName(&slow, name);
341  TailCallBuiltin(masm(), SlowBuiltin(kind()));
342  return GetCode(kind(), Code::FAST, name);
343}
344
345
346Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) {
347  Label miss;
348  GenerateStoreField(it, value(), &miss);
349  __ bind(&miss);
350  TailCallBuiltin(masm(), MissBuiltin(kind()));
351  return GetCode(kind(), Code::FAST, it->name());
352}
353
354
355Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter(
356    Handle<JSObject> object, Handle<Name> name, Handle<JSFunction> setter) {
357  Frontend(receiver(), name);
358  GenerateStoreViaSetter(masm(), type(), receiver(), setter);
359
360  return GetCode(kind(), Code::FAST, name);
361}
362
363
364Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback(
365    Handle<JSObject> object, Handle<Name> name,
366    const CallOptimization& call_optimization) {
367  Frontend(receiver(), name);
368  Register values[] = {value()};
369  GenerateFastApiCall(masm(), call_optimization, handle(object->map()),
370                      receiver(), scratch1(), true, 1, values);
371  return GetCode(kind(), Code::FAST, name);
372}
373
374
375#undef __
376
377
378void ElementHandlerCompiler::CompileElementHandlers(
379    MapHandleList* receiver_maps, CodeHandleList* handlers) {
380  for (int i = 0; i < receiver_maps->length(); ++i) {
381    Handle<Map> receiver_map = receiver_maps->at(i);
382    Handle<Code> cached_stub;
383
384    if ((receiver_map->instance_type() & kNotStringTag) == 0) {
385      cached_stub = isolate()->builtins()->KeyedLoadIC_String();
386    } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) {
387      cached_stub = isolate()->builtins()->KeyedLoadIC_Slow();
388    } else {
389      bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
390      ElementsKind elements_kind = receiver_map->elements_kind();
391      if (receiver_map->has_indexed_interceptor()) {
392        cached_stub = LoadIndexedInterceptorStub(isolate()).GetCode();
393      } else if (IsSloppyArgumentsElements(elements_kind)) {
394        cached_stub = KeyedLoadSloppyArgumentsStub(isolate()).GetCode();
395      } else if (IsFastElementsKind(elements_kind) ||
396                 IsExternalArrayElementsKind(elements_kind) ||
397                 IsFixedTypedArrayElementsKind(elements_kind)) {
398        cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind)
399                          .GetCode();
400      } else {
401        DCHECK(elements_kind == DICTIONARY_ELEMENTS);
402        cached_stub = LoadDictionaryElementStub(isolate()).GetCode();
403      }
404    }
405
406    handlers->Add(cached_stub);
407  }
408}
409}
410}  // namespace v8::internal
411