// 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