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