| // Copyright 2014 the V8 project authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "src/ic/call-optimization.h" | |
| #include "src/objects-inl.h" | |
| namespace v8 { | |
| namespace internal { | |
| CallOptimization::CallOptimization(Isolate* isolate, Handle<Object> function) { | |
| constant_function_ = Handle<JSFunction>::null(); | |
| is_simple_api_call_ = false; | |
| expected_receiver_type_ = Handle<FunctionTemplateInfo>::null(); | |
| api_call_info_ = Handle<CallHandlerInfo>::null(); | |
| if (function->IsJSFunction()) { | |
| Initialize(isolate, Handle<JSFunction>::cast(function)); | |
| } else if (function->IsFunctionTemplateInfo()) { | |
| Initialize(isolate, Handle<FunctionTemplateInfo>::cast(function)); | |
| } | |
| } | |
| Context* CallOptimization::GetAccessorContext(Map* holder_map) const { | |
| if (is_constant_call()) { | |
| return constant_function_->context()->native_context(); | |
| } | |
| JSFunction* constructor = JSFunction::cast(holder_map->GetConstructor()); | |
| return constructor->context()->native_context(); | |
| } | |
| bool CallOptimization::IsCrossContextLazyAccessorPair(Context* native_context, | |
| Map* holder_map) const { | |
| DCHECK(native_context->IsNativeContext()); | |
| if (is_constant_call()) return false; | |
| return native_context != GetAccessorContext(holder_map); | |
| } | |
| Handle<JSObject> CallOptimization::LookupHolderOfExpectedType( | |
| Handle<Map> object_map, HolderLookup* holder_lookup) const { | |
| DCHECK(is_simple_api_call()); | |
| if (!object_map->IsJSObjectMap()) { | |
| *holder_lookup = kHolderNotFound; | |
| return Handle<JSObject>::null(); | |
| } | |
| if (expected_receiver_type_.is_null() || | |
| expected_receiver_type_->IsTemplateFor(*object_map)) { | |
| *holder_lookup = kHolderIsReceiver; | |
| return Handle<JSObject>::null(); | |
| } | |
| if (object_map->has_hidden_prototype()) { | |
| JSObject* raw_prototype = JSObject::cast(object_map->prototype()); | |
| Handle<JSObject> prototype(raw_prototype, raw_prototype->GetIsolate()); | |
| object_map = handle(prototype->map(), prototype->GetIsolate()); | |
| if (expected_receiver_type_->IsTemplateFor(*object_map)) { | |
| *holder_lookup = kHolderFound; | |
| return prototype; | |
| } | |
| } | |
| *holder_lookup = kHolderNotFound; | |
| return Handle<JSObject>::null(); | |
| } | |
| bool CallOptimization::IsCompatibleReceiver(Handle<Object> receiver, | |
| Handle<JSObject> holder) const { | |
| DCHECK(is_simple_api_call()); | |
| if (!receiver->IsHeapObject()) return false; | |
| Handle<Map> map(HeapObject::cast(*receiver)->map(), holder->GetIsolate()); | |
| return IsCompatibleReceiverMap(map, holder); | |
| } | |
| bool CallOptimization::IsCompatibleReceiverMap(Handle<Map> map, | |
| Handle<JSObject> holder) const { | |
| HolderLookup holder_lookup; | |
| Handle<JSObject> api_holder = LookupHolderOfExpectedType(map, &holder_lookup); | |
| switch (holder_lookup) { | |
| case kHolderNotFound: | |
| return false; | |
| case kHolderIsReceiver: | |
| return true; | |
| case kHolderFound: | |
| if (api_holder.is_identical_to(holder)) return true; | |
| // Check if holder is in prototype chain of api_holder. | |
| { | |
| JSObject* object = *api_holder; | |
| while (true) { | |
| Object* prototype = object->map()->prototype(); | |
| if (!prototype->IsJSObject()) return false; | |
| if (prototype == *holder) return true; | |
| object = JSObject::cast(prototype); | |
| } | |
| } | |
| break; | |
| } | |
| UNREACHABLE(); | |
| } | |
| void CallOptimization::Initialize( | |
| Isolate* isolate, Handle<FunctionTemplateInfo> function_template_info) { | |
| if (function_template_info->call_code()->IsUndefined(isolate)) return; | |
| api_call_info_ = handle( | |
| CallHandlerInfo::cast(function_template_info->call_code()), isolate); | |
| if (!function_template_info->signature()->IsUndefined(isolate)) { | |
| expected_receiver_type_ = | |
| handle(FunctionTemplateInfo::cast(function_template_info->signature()), | |
| isolate); | |
| } | |
| is_simple_api_call_ = true; | |
| } | |
| void CallOptimization::Initialize(Isolate* isolate, | |
| Handle<JSFunction> function) { | |
| if (function.is_null() || !function->is_compiled()) return; | |
| constant_function_ = function; | |
| AnalyzePossibleApiFunction(isolate, function); | |
| } | |
| void CallOptimization::AnalyzePossibleApiFunction(Isolate* isolate, | |
| Handle<JSFunction> function) { | |
| if (!function->shared()->IsApiFunction()) return; | |
| Handle<FunctionTemplateInfo> info(function->shared()->get_api_func_data(), | |
| isolate); | |
| // Require a C++ callback. | |
| if (info->call_code()->IsUndefined(isolate)) return; | |
| api_call_info_ = handle(CallHandlerInfo::cast(info->call_code()), isolate); | |
| if (!info->signature()->IsUndefined(isolate)) { | |
| expected_receiver_type_ = | |
| handle(FunctionTemplateInfo::cast(info->signature()), isolate); | |
| } | |
| is_simple_api_call_ = true; | |
| } | |
| } // namespace internal | |
| } // namespace v8 |