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/call-optimization.h"
6 #include "src/objects-inl.h"
7 
8 namespace v8 {
9 namespace internal {
10 
CallOptimization(Handle<Object> function)11 CallOptimization::CallOptimization(Handle<Object> function) {
12   constant_function_ = Handle<JSFunction>::null();
13   is_simple_api_call_ = false;
14   expected_receiver_type_ = Handle<FunctionTemplateInfo>::null();
15   api_call_info_ = Handle<CallHandlerInfo>::null();
16   if (function->IsJSFunction()) {
17     Initialize(Handle<JSFunction>::cast(function));
18   } else if (function->IsFunctionTemplateInfo()) {
19     Initialize(Handle<FunctionTemplateInfo>::cast(function));
20   }
21 }
22 
23 
LookupHolderOfExpectedType(Handle<Map> object_map,HolderLookup * holder_lookup,int * holder_depth_in_prototype_chain) const24 Handle<JSObject> CallOptimization::LookupHolderOfExpectedType(
25     Handle<Map> object_map, HolderLookup* holder_lookup,
26     int* holder_depth_in_prototype_chain) const {
27   DCHECK(is_simple_api_call());
28   if (!object_map->IsJSObjectMap()) {
29     *holder_lookup = kHolderNotFound;
30     return Handle<JSObject>::null();
31   }
32   if (expected_receiver_type_.is_null() ||
33       expected_receiver_type_->IsTemplateFor(*object_map)) {
34     *holder_lookup = kHolderIsReceiver;
35     return Handle<JSObject>::null();
36   }
37   for (int depth = 1; true; depth++) {
38     if (!object_map->has_hidden_prototype()) break;
39     Handle<JSObject> prototype(JSObject::cast(object_map->prototype()));
40     object_map = handle(prototype->map());
41     if (expected_receiver_type_->IsTemplateFor(*object_map)) {
42       *holder_lookup = kHolderFound;
43       if (holder_depth_in_prototype_chain != NULL) {
44         *holder_depth_in_prototype_chain = depth;
45       }
46       return prototype;
47     }
48   }
49   *holder_lookup = kHolderNotFound;
50   return Handle<JSObject>::null();
51 }
52 
53 
IsCompatibleReceiver(Handle<Object> receiver,Handle<JSObject> holder) const54 bool CallOptimization::IsCompatibleReceiver(Handle<Object> receiver,
55                                             Handle<JSObject> holder) const {
56   DCHECK(is_simple_api_call());
57   if (!receiver->IsHeapObject()) return false;
58   Handle<Map> map(HeapObject::cast(*receiver)->map());
59   return IsCompatibleReceiverMap(map, holder);
60 }
61 
62 
IsCompatibleReceiverMap(Handle<Map> map,Handle<JSObject> holder) const63 bool CallOptimization::IsCompatibleReceiverMap(Handle<Map> map,
64                                                Handle<JSObject> holder) const {
65   HolderLookup holder_lookup;
66   Handle<JSObject> api_holder = LookupHolderOfExpectedType(map, &holder_lookup);
67   switch (holder_lookup) {
68     case kHolderNotFound:
69       return false;
70     case kHolderIsReceiver:
71       return true;
72     case kHolderFound:
73       if (api_holder.is_identical_to(holder)) return true;
74       // Check if holder is in prototype chain of api_holder.
75       {
76         JSObject* object = *api_holder;
77         while (true) {
78           Object* prototype = object->map()->prototype();
79           if (!prototype->IsJSObject()) return false;
80           if (prototype == *holder) return true;
81           object = JSObject::cast(prototype);
82         }
83       }
84       break;
85   }
86   UNREACHABLE();
87   return false;
88 }
89 
Initialize(Handle<FunctionTemplateInfo> function_template_info)90 void CallOptimization::Initialize(
91     Handle<FunctionTemplateInfo> function_template_info) {
92   Isolate* isolate = function_template_info->GetIsolate();
93   if (function_template_info->call_code()->IsUndefined(isolate)) return;
94   api_call_info_ =
95       handle(CallHandlerInfo::cast(function_template_info->call_code()));
96 
97   if (!function_template_info->signature()->IsUndefined(isolate)) {
98     expected_receiver_type_ =
99         handle(FunctionTemplateInfo::cast(function_template_info->signature()));
100   }
101   is_simple_api_call_ = true;
102 }
103 
Initialize(Handle<JSFunction> function)104 void CallOptimization::Initialize(Handle<JSFunction> function) {
105   if (function.is_null() || !function->is_compiled()) return;
106 
107   constant_function_ = function;
108   AnalyzePossibleApiFunction(function);
109 }
110 
111 
AnalyzePossibleApiFunction(Handle<JSFunction> function)112 void CallOptimization::AnalyzePossibleApiFunction(Handle<JSFunction> function) {
113   if (!function->shared()->IsApiFunction()) return;
114   Isolate* isolate = function->GetIsolate();
115   Handle<FunctionTemplateInfo> info(function->shared()->get_api_func_data(),
116                                     isolate);
117 
118   // Require a C++ callback.
119   if (info->call_code()->IsUndefined(isolate)) return;
120   api_call_info_ = handle(CallHandlerInfo::cast(info->call_code()), isolate);
121 
122   if (!info->signature()->IsUndefined(isolate)) {
123     expected_receiver_type_ =
124         handle(FunctionTemplateInfo::cast(info->signature()), isolate);
125   }
126 
127   is_simple_api_call_ = true;
128 }
129 }  // namespace internal
130 }  // namespace v8
131