Commit 2367439b authored by Yuki Shiino's avatar Yuki Shiino Committed by Commit Bot

v8bindings: Refactor v8::Template cache of V8PerIsolateData

Unifies the interface template cache and operation template
cache in V8PerIsolateData into a single v8::Template cache,
which will be used to store any sort of v8::Template, e.g.
IDL interface object's template, IDL namespace object's
template, JS function's template, etc.

This is a step to support IDL namespace.

Bug: 839389
Change-Id: I4130c713665fcb8555ea188f2e582035468ade83
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2463092
Commit-Queue: Yuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarHitoshi Yoshida <peria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#818911}
parent d4b46d56
......@@ -269,11 +269,9 @@ v8::MaybeLocal<v8::Value> CreateNamedConstructorFunction(
return v8::Undefined(isolate);
}
// Named constructors are not interface objcets (despite that they're
// pretending so), but we reuse the cache of interface objects, which just
// works because both are V8 function template.
v8::Local<v8::FunctionTemplate> function_template =
per_isolate_data->FindInterfaceTemplate(world, callback_key);
per_isolate_data->FindV8Template(world, callback_key)
.As<v8::FunctionTemplate>();
if (function_template.IsEmpty()) {
function_template = v8::FunctionTemplate::New(
isolate, callback, v8::Local<v8::Value>(), v8::Local<v8::Signature>(),
......@@ -285,8 +283,7 @@ v8::MaybeLocal<v8::Value> CreateNamedConstructorFunction(
function_template->SetClassName(V8AtomicString(isolate, func_name));
function_template->InstanceTemplate()->SetInternalFieldCount(
kV8DefaultWrapperInternalFieldCount);
per_isolate_data->SetInterfaceTemplate(world, callback_key,
function_template);
per_isolate_data->AddV8Template(world, callback_key, function_template);
}
v8::Local<v8::Context> context = script_state->GetContext();
......
......@@ -906,7 +906,7 @@ v8::Local<v8::FunctionTemplate> V8DOMConfiguration::DomClassTemplate(
InstallTemplateFunction configure_dom_class_template) {
V8PerIsolateData* data = V8PerIsolateData::From(isolate);
v8::Local<v8::FunctionTemplate> interface_template =
data->FindInterfaceTemplate(world, wrapper_type_info);
data->FindV8Template(world, wrapper_type_info).As<v8::FunctionTemplate>();
if (!interface_template.IsEmpty())
return interface_template;
......@@ -914,7 +914,7 @@ v8::Local<v8::FunctionTemplate> V8DOMConfiguration::DomClassTemplate(
interface_template = v8::FunctionTemplate::New(
isolate, V8ObjectConstructor::IsValidConstructorMode);
configure_dom_class_template(isolate, world, interface_template);
data->SetInterfaceTemplate(world, wrapper_type_info, interface_template);
data->AddV8Template(world, wrapper_type_info, interface_template);
return interface_template;
}
......
......@@ -413,8 +413,8 @@ void V8ContextSnapshotImpl::InstallInterfaceTemplates(v8::Isolate* isolate) {
->GetDataFromSnapshotOnce<v8::FunctionTemplate>(
world_index * base::size(type_info_table) + i)
.ToLocalChecked();
per_isolate_data->SetInterfaceTemplate(
*world, type_info.wrapper_type_info, interface_template);
per_isolate_data->AddV8Template(*world, type_info.wrapper_type_info,
interface_template);
type_info.install_props_per_isolate(
isolate, *world, interface_template->InstanceTemplate(),
interface_template->PrototypeTemplate(), interface_template);
......
......@@ -22,23 +22,21 @@ v8::MaybeLocal<v8::Function> GetCrossOriginFunction(
V8PerIsolateData* per_isolate_data = V8PerIsolateData::From(isolate);
const void* callback_key = reinterpret_cast<const void*>(callback);
// ES functions accessible across origins are not interface objects, but we
// reuse the cache of interface objects, which just works because both are
// V8 function template.
v8::Local<v8::FunctionTemplate> function_template =
per_isolate_data->FindInterfaceTemplate(script_state->World(),
callback_key);
per_isolate_data->FindV8Template(script_state->World(), callback_key)
.As<v8::FunctionTemplate>();
if (function_template.IsEmpty()) {
v8::Local<v8::FunctionTemplate> interface_template =
per_isolate_data->FindInterfaceTemplate(script_state->World(),
wrapper_type_info);
per_isolate_data
->FindV8Template(script_state->World(), wrapper_type_info)
.As<v8::FunctionTemplate>();
v8::Local<v8::Signature> signature =
v8::Signature::New(isolate, interface_template);
function_template = v8::FunctionTemplate::New(
isolate, callback, v8::Local<v8::Value>(), signature, func_length,
v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect);
per_isolate_data->SetInterfaceTemplate(script_state->World(), callback_key,
function_template);
per_isolate_data->AddV8Template(script_state->World(), callback_key,
function_template);
}
return function_template->GetFunction(current_context);
}
......
......@@ -86,7 +86,8 @@ bool V8DOMWrapper::IsWrapper(v8::Isolate* isolate, v8::Local<v8::Value> value) {
V8PerIsolateData* per_isolate_data = V8PerIsolateData::From(isolate);
if (!(untrusted_wrapper_type_info && per_isolate_data))
return false;
return per_isolate_data->HasInstance(untrusted_wrapper_type_info, object);
return per_isolate_data->HasInstanceOfUntrustedType(
untrusted_wrapper_type_info, object);
}
bool V8DOMWrapper::HasInternalFieldsSet(v8::Local<v8::Value> value) {
......
......@@ -90,8 +90,11 @@ V8PerIsolateData::V8PerIsolateData(
// This constructor is used for creating a V8 context snapshot. It must run on
// the main thread.
V8PerIsolateData::V8PerIsolateData()
: v8_context_snapshot_mode_(V8ContextSnapshotMode::kTakeSnapshot),
// TODO(yukishiino): This constructor may not be necessary. Probably We can
// reuse V8PerIsolateData(task_runner, v8_context_snapshot_mode) constructor.
V8PerIsolateData::V8PerIsolateData(
V8ContextSnapshotMode v8_context_snapshot_mode)
: v8_context_snapshot_mode_(v8_context_snapshot_mode),
isolate_holder_(Thread::Current()->GetTaskRunner(),
gin::IsolateHolder::kSingleThread,
gin::IsolateHolder::kAllowAtomicsWait,
......@@ -104,6 +107,7 @@ V8PerIsolateData::V8PerIsolateData()
is_handling_recursion_level_error_(false),
runtime_call_stats_(base::DefaultTickClock::GetInstance()) {
CHECK(IsMainThread());
CHECK_EQ(v8_context_snapshot_mode_, V8ContextSnapshotMode::kTakeSnapshot);
// SnapshotCreator enters the isolate, so we don't call Isolate::Enter() here.
g_main_thread_per_isolate_data = this;
......@@ -121,7 +125,7 @@ v8::Isolate* V8PerIsolateData::Initialize(
V8ContextSnapshotMode context_mode) {
V8PerIsolateData* data = nullptr;
if (context_mode == V8ContextSnapshotMode::kTakeSnapshot) {
data = new V8PerIsolateData();
data = new V8PerIsolateData(context_mode);
} else {
data = new V8PerIsolateData(task_runner, context_mode);
}
......@@ -190,10 +194,8 @@ void V8PerIsolateData::Destroy(v8::Isolate* isolate) {
data->private_property_.reset();
data->string_cache_->Dispose();
data->string_cache_.reset();
data->interface_template_map_for_non_main_world_.clear();
data->interface_template_map_for_main_world_.clear();
data->operation_template_map_for_non_main_world_.clear();
data->operation_template_map_for_main_world_.clear();
data->v8_template_map_for_main_world_.clear();
data->v8_template_map_for_non_main_worlds_.clear();
if (IsMainThread())
g_main_thread_per_isolate_data = nullptr;
......@@ -202,60 +204,80 @@ void V8PerIsolateData::Destroy(v8::Isolate* isolate) {
delete data;
}
V8PerIsolateData::V8FunctionTemplateMap&
V8PerIsolateData::SelectInterfaceTemplateMap(const DOMWrapperWorld& world) {
return world.IsMainWorld() ? interface_template_map_for_main_world_
: interface_template_map_for_non_main_world_;
}
V8PerIsolateData::V8FunctionTemplateMap&
V8PerIsolateData::SelectOperationTemplateMap(const DOMWrapperWorld& world) {
return world.IsMainWorld() ? operation_template_map_for_main_world_
: operation_template_map_for_non_main_world_;
}
v8::Local<v8::FunctionTemplate> V8PerIsolateData::FindOrCreateOperationTemplate(
v8::Local<v8::Template> V8PerIsolateData::FindV8Template(
const DOMWrapperWorld& world,
const void* key,
v8::FunctionCallback callback,
v8::Local<v8::Value> data,
v8::Local<v8::Signature> signature,
int length) {
auto& map = SelectOperationTemplateMap(world);
const void* key) {
auto& map = SelectV8TemplateMap(world);
auto result = map.find(key);
if (result != map.end())
return result->value.Get(GetIsolate());
return v8::Local<v8::Template>();
}
v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(
GetIsolate(), callback, data, signature, length);
templ->RemovePrototype();
map.insert(key, v8::Eternal<v8::FunctionTemplate>(GetIsolate(), templ));
return templ;
void V8PerIsolateData::AddV8Template(const DOMWrapperWorld& world,
const void* key,
v8::Local<v8::Template> value) {
auto& map = SelectV8TemplateMap(world);
auto result = map.insert(key, v8::Eternal<v8::Template>(GetIsolate(), value));
DCHECK(result.is_new_entry);
}
v8::Local<v8::FunctionTemplate> V8PerIsolateData::FindInterfaceTemplate(
const DOMWrapperWorld& world,
const void* key) {
auto& map = SelectInterfaceTemplateMap(world);
auto result = map.find(key);
if (result != map.end())
return result->value.Get(GetIsolate());
return v8::Local<v8::FunctionTemplate>();
bool V8PerIsolateData::HasInstance(const WrapperTypeInfo* wrapper_type_info,
v8::Local<v8::Value> untrusted_value) {
RUNTIME_CALL_TIMER_SCOPE(GetIsolate(),
RuntimeCallStats::CounterId::kHasInstance);
return HasInstance(wrapper_type_info, untrusted_value,
v8_template_map_for_main_world_) ||
HasInstance(wrapper_type_info, untrusted_value,
v8_template_map_for_non_main_worlds_);
}
void V8PerIsolateData::SetInterfaceTemplate(
const DOMWrapperWorld& world,
const void* key,
v8::Local<v8::FunctionTemplate> value) {
auto& map = SelectInterfaceTemplateMap(world);
map.insert(key, v8::Eternal<v8::FunctionTemplate>(GetIsolate(), value));
bool V8PerIsolateData::HasInstance(const WrapperTypeInfo* wrapper_type_info,
v8::Local<v8::Value> untrusted_value,
const V8TemplateMap& map) {
auto result = map.find(wrapper_type_info);
if (result == map.end())
return false;
v8::Local<v8::Template> v8_template = result->value.Get(GetIsolate());
DCHECK(v8_template->IsFunctionTemplate());
return v8_template.As<v8::FunctionTemplate>()->HasInstance(untrusted_value);
}
bool V8PerIsolateData::HasInstanceOfUntrustedType(
const WrapperTypeInfo* untrusted_wrapper_type_info,
v8::Local<v8::Value> untrusted_value) {
RUNTIME_CALL_TIMER_SCOPE(GetIsolate(),
RuntimeCallStats::CounterId::kHasInstance);
return HasInstanceOfUntrustedType(untrusted_wrapper_type_info,
untrusted_value,
v8_template_map_for_main_world_) ||
HasInstanceOfUntrustedType(untrusted_wrapper_type_info,
untrusted_value,
v8_template_map_for_non_main_worlds_);
}
bool V8PerIsolateData::HasInstanceOfUntrustedType(
const WrapperTypeInfo* untrusted_wrapper_type_info,
v8::Local<v8::Value> untrusted_value,
const V8TemplateMap& map) {
auto result = map.find(untrusted_wrapper_type_info);
if (result == map.end())
return false;
v8::Local<v8::Template> v8_template = result->value.Get(GetIsolate());
if (!v8_template->IsFunctionTemplate())
return false;
return v8_template.As<v8::FunctionTemplate>()->HasInstance(untrusted_value);
}
V8PerIsolateData::V8TemplateMap& V8PerIsolateData::SelectV8TemplateMap(
const DOMWrapperWorld& world) {
return world.IsMainWorld() ? v8_template_map_for_main_world_
: v8_template_map_for_non_main_worlds_;
}
void V8PerIsolateData::ClearPersistentsForV8ContextSnapshot() {
interface_template_map_for_main_world_.clear();
interface_template_map_for_non_main_world_.clear();
operation_template_map_for_main_world_.clear();
operation_template_map_for_non_main_world_.clear();
v8_template_map_for_main_world_.clear();
v8_template_map_for_non_main_worlds_.clear();
eternal_name_cache_.clear();
private_property_.reset();
}
......@@ -303,53 +325,6 @@ void V8PerIsolateData::ClearScriptRegexpContext() {
script_regexp_script_state_ = nullptr;
}
bool V8PerIsolateData::HasInstance(
const WrapperTypeInfo* untrusted_wrapper_type_info,
v8::Local<v8::Value> value) {
RUNTIME_CALL_TIMER_SCOPE(GetIsolate(),
RuntimeCallStats::CounterId::kHasInstance);
return HasInstance(untrusted_wrapper_type_info, value,
interface_template_map_for_main_world_) ||
HasInstance(untrusted_wrapper_type_info, value,
interface_template_map_for_non_main_world_);
}
bool V8PerIsolateData::HasInstance(
const WrapperTypeInfo* untrusted_wrapper_type_info,
v8::Local<v8::Value> value,
V8FunctionTemplateMap& map) {
auto result = map.find(untrusted_wrapper_type_info);
if (result == map.end())
return false;
v8::Local<v8::FunctionTemplate> templ = result->value.Get(GetIsolate());
return templ->HasInstance(value);
}
v8::Local<v8::Object> V8PerIsolateData::FindInstanceInPrototypeChain(
const WrapperTypeInfo* info,
v8::Local<v8::Value> value) {
v8::Local<v8::Object> wrapper = FindInstanceInPrototypeChain(
info, value, interface_template_map_for_main_world_);
if (!wrapper.IsEmpty())
return wrapper;
return FindInstanceInPrototypeChain(
info, value, interface_template_map_for_non_main_world_);
}
v8::Local<v8::Object> V8PerIsolateData::FindInstanceInPrototypeChain(
const WrapperTypeInfo* info,
v8::Local<v8::Value> value,
V8FunctionTemplateMap& map) {
if (value.IsEmpty() || !value->IsObject())
return v8::Local<v8::Object>();
auto result = map.find(info);
if (result == map.end())
return v8::Local<v8::Object>();
v8::Local<v8::FunctionTemplate> templ = result->value.Get(GetIsolate());
return v8::Local<v8::Object>::Cast(value)->FindInstanceInPrototypeChain(
templ);
}
void V8PerIsolateData::AddEndOfScopeTask(base::OnceClosure task) {
end_of_scope_tasks_.push_back(std::move(task));
}
......
......@@ -45,7 +45,7 @@
namespace base {
class SingleThreadTaskRunner;
}
} // namespace base
namespace blink {
......@@ -57,7 +57,7 @@ struct WrapperTypeInfo;
// Used to hold data that is associated with a single v8::Isolate object, and
// has a 1:1 relationship with v8::Isolate.
class PLATFORM_EXPORT V8PerIsolateData {
class PLATFORM_EXPORT V8PerIsolateData final {
USING_FAST_MALLOC(V8PerIsolateData);
public:
......@@ -144,12 +144,18 @@ class PLATFORM_EXPORT V8PerIsolateData {
V8PrivateProperty* PrivateProperty() { return private_property_.get(); }
// Accessors to the cache of interface templates.
v8::Local<v8::FunctionTemplate> FindInterfaceTemplate(const DOMWrapperWorld&,
const void* key);
void SetInterfaceTemplate(const DOMWrapperWorld&,
const void* key,
v8::Local<v8::FunctionTemplate>);
// Accessors to the cache of v8::Templates.
v8::Local<v8::Template> FindV8Template(const DOMWrapperWorld& world,
const void* key);
void AddV8Template(const DOMWrapperWorld& world,
const void* key,
v8::Local<v8::Template> value);
bool HasInstance(const WrapperTypeInfo* wrapper_type_info,
v8::Local<v8::Value> untrusted_value);
bool HasInstanceOfUntrustedType(
const WrapperTypeInfo* untrusted_wrapper_type_info,
v8::Local<v8::Value> untrusted_value);
// When v8::SnapshotCreator::CreateBlob() is called, we must not have
// persistent handles in Blink. This method clears them.
......@@ -161,20 +167,6 @@ class PLATFORM_EXPORT V8PerIsolateData {
V8ContextSnapshotMode GetV8ContextSnapshotMode() const {
return v8_context_snapshot_mode_;
}
void BailoutAndDisableV8ContextSnapshot() {
DCHECK_EQ(V8ContextSnapshotMode::kUseSnapshot, v8_context_snapshot_mode_);
v8_context_snapshot_mode_ = V8ContextSnapshotMode::kDontUseSnapshot;
}
// Accessor to the cache of cross-origin accessible operation's templates.
// Created templates get automatically cached.
v8::Local<v8::FunctionTemplate> FindOrCreateOperationTemplate(
const DOMWrapperWorld&,
const void* key,
v8::FunctionCallback,
v8::Local<v8::Value> data,
v8::Local<v8::Signature>,
int length);
// Obtains a pointer to an array of names, given a lookup key. If it does not
// yet exist, it is created from the given array of strings. Once created,
......@@ -184,10 +176,6 @@ class PLATFORM_EXPORT V8PerIsolateData {
const void* lookup_key,
const base::span<const char* const>& names);
bool HasInstance(const WrapperTypeInfo* untrusted, v8::Local<v8::Value>);
v8::Local<v8::Object> FindInstanceInPrototypeChain(const WrapperTypeInfo*,
v8::Local<v8::Value>);
v8::Local<v8::Context> EnsureScriptRegexpContext();
void ClearScriptRegexpContext();
......@@ -222,43 +210,38 @@ class PLATFORM_EXPORT V8PerIsolateData {
private:
V8PerIsolateData(scoped_refptr<base::SingleThreadTaskRunner>,
V8ContextSnapshotMode);
V8PerIsolateData();
explicit V8PerIsolateData(V8ContextSnapshotMode);
~V8PerIsolateData();
// A really simple hash function, which makes lookups faster. The set of
// possible keys for this is relatively small and fixed at compile time, so
// collisions are less of a worry than they would otherwise be.
struct SimplePtrHash : WTF::PtrHash<const void> {
struct SimplePtrHash final : public WTF::PtrHash<const void> {
static unsigned GetHash(const void* key) {
uintptr_t k = reinterpret_cast<uintptr_t>(key);
return static_cast<unsigned>(k ^ (k >> 8));
}
};
using V8FunctionTemplateMap =
HashMap<const void*, v8::Eternal<v8::FunctionTemplate>, SimplePtrHash>;
V8FunctionTemplateMap& SelectInterfaceTemplateMap(const DOMWrapperWorld&);
V8FunctionTemplateMap& SelectOperationTemplateMap(const DOMWrapperWorld&);
bool HasInstance(const WrapperTypeInfo* untrusted,
v8::Local<v8::Value>,
V8FunctionTemplateMap&);
v8::Local<v8::Object> FindInstanceInPrototypeChain(const WrapperTypeInfo*,
v8::Local<v8::Value>,
V8FunctionTemplateMap&);
using V8TemplateMap =
HashMap<const void*, v8::Eternal<v8::Template>, SimplePtrHash>;
V8TemplateMap& SelectV8TemplateMap(const DOMWrapperWorld&);
bool HasInstance(const WrapperTypeInfo* wrapper_type_info,
v8::Local<v8::Value> untrusted_value,
const V8TemplateMap& map);
bool HasInstanceOfUntrustedType(
const WrapperTypeInfo* untrusted_wrapper_type_info,
v8::Local<v8::Value> untrusted_value,
const V8TemplateMap& map);
V8ContextSnapshotMode v8_context_snapshot_mode_;
// This isolate_holder_ must be initialized before initializing some other
// members below.
gin::IsolateHolder isolate_holder_;
// interface_template_map_for_{,non_}main_world holds function templates for
// the inerface objects.
V8FunctionTemplateMap interface_template_map_for_main_world_;
V8FunctionTemplateMap interface_template_map_for_non_main_world_;
// m_operationTemplateMapFor{,Non}MainWorld holds function templates for
// the cross-origin accessible DOM operations.
V8FunctionTemplateMap operation_template_map_for_main_world_;
V8FunctionTemplateMap operation_template_map_for_non_main_world_;
// v8::Template cache of interface objects, namespace objects, etc.
V8TemplateMap v8_template_map_for_main_world_;
V8TemplateMap v8_template_map_for_non_main_worlds_;
// Contains lists of eternal names, such as dictionary keys.
HashMap<const void*, Vector<v8::Eternal<v8::Name>>> eternal_name_cache_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment