From 54afe29f866e4c3878242de7a78941fc363791e9 Mon Sep 17 00:00:00 2001 From: peria Date: Fri, 28 Jul 2017 01:54:56 -0700 Subject: [PATCH] Create and use V8 context snapshots. This CL does two things. 1. In compile time, creates a snapshot file, which consists of V8 contexts. 2. Creates v8::Context from the snapshot in LocalWindowProxy::CreateContext(). We expect this speeds up context creation for 3 times faster on Android. Detailed information is described in the design doc [1]. [1] Design doc: https://docs.google.com/document/d/1jpQQX0piaxcHJPWakp_Kr_03g5Gnma5h5-Kdlqu7jVQ/edit#heading=h.k6iklq6rvd30 Test expectations are changed due to http://crbug.com/705364 BUG=588893, 617892, 705364 Review-Url: https://codereview.chromium.org/2841443005 Cr-Commit-Position: refs/heads/master@{#490329} --- build/config/features.gni | 3 +- chrome/BUILD.gn | 3 + content/app/content_main_runner.cc | 52 +- .../app/mojo/content_renderer_manifest.json | 8 +- .../public/common/content_descriptor_keys.cc | 4 +- .../public/common/content_descriptor_keys.h | 1 + content/shell/BUILD.gn | 10 + extensions/shell/BUILD.gn | 2 + gin/isolate_holder.cc | 81 +-- gin/public/isolate_holder.h | 8 +- gin/v8_initializer.cc | 97 +++- gin/v8_initializer.h | 14 + .../device-emulation-320-2x-expected.txt | 2 +- .../device-emulation-320-expected.txt | 2 +- ...e-emulation-320-only-viewport-expected.txt | 2 +- .../device-emulation-980-2x-expected.txt | 2 +- .../device-emulation-980-expected.txt | 2 +- ...e-emulation-980-only-viewport-expected.txt | 2 +- .../device-emulation-controls-expected.txt | 2 +- .../device-emulation-dw-2x-expected.txt | 2 +- .../device-emulation-dw-expected.txt | 2 +- ...evice-emulation-initial-scale-expected.txt | 2 +- .../device-emulation-insets-expected.txt | 2 +- .../device-emulation-none-2x-expected.txt | 2 +- .../device-emulation-none-expected.txt | 2 +- .../device-emulation-restore-expected.txt | 2 +- .../device-emulation-small-dw-expected.txt | 2 +- .../device-emulation-small-expected.txt | 2 +- .../WebKit/Source/bindings/bindings.gni | 2 + .../bindings/core/v8/LocalWindowProxy.cpp | 80 ++- .../bindings/core/v8/V8ContextSnapshot.cpp | 501 ++++++++++++++++++ .../bindings/core/v8/V8ContextSnapshot.h | 79 +++ .../bindings/core/v8/V8DOMConfiguration.cpp | 23 +- .../Source/bindings/core/v8/V8Initializer.cpp | 22 +- .../Source/bindings/core/v8/V8Initializer.h | 2 +- .../Source/bindings/modules/v8/BUILD.gn | 35 ++ .../v8/V8ContextSnapshotExternalReferences.h | 29 + .../Source/bindings/modules/v8/generated.gni | 3 + .../WebKit/Source/bindings/modules/v8/v8.gni | 1 + ...v8_context_snapshot_external_references.py | 223 ++++++++ .../external_reference_table.cpp.tmpl | 125 +++++ .../Source/bindings/templates/templates.gni | 5 +- .../Source/controller/BlinkInitializer.cpp | 5 +- .../WebKit/Source/core/exported/BUILD.gn | 1 + .../core/exported/WebV8ContextSnapshot.cpp | 16 + .../core/workers/WorkerBackingThread.cpp | 7 +- third_party/WebKit/Source/modules/BUILD.gn | 4 + .../platform/RuntimeEnabledFeatures.json5 | 4 + .../platform/bindings/DOMWrapperWorld.cpp | 2 + .../platform/bindings/DOMWrapperWorld.h | 1 + .../platform/bindings/ScriptWrappable.h | 2 +- .../platform/bindings/V8PerContextData.cpp | 2 - .../platform/bindings/V8PerIsolateData.cpp | 78 ++- .../platform/bindings/V8PerIsolateData.h | 46 +- third_party/WebKit/public/BUILD.gn | 1 + third_party/WebKit/public/platform/Platform.h | 7 + .../WebKit/public/web/WebV8ContextSnapshot.h | 23 + tools/v8_context_snapshot/BUILD.gn | 97 ++++ tools/v8_context_snapshot/DEPS | 7 + tools/v8_context_snapshot/OWNERS | 6 + tools/v8_context_snapshot/run.py | 15 + .../v8_context_snapshot_generator.cc | 69 +++ 62 files changed, 1676 insertions(+), 162 deletions(-) create mode 100644 third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp create mode 100644 third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.h create mode 100644 third_party/WebKit/Source/bindings/modules/v8/V8ContextSnapshotExternalReferences.h create mode 100644 third_party/WebKit/Source/bindings/scripts/generate_v8_context_snapshot_external_references.py create mode 100644 third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl create mode 100644 third_party/WebKit/Source/core/exported/WebV8ContextSnapshot.cpp create mode 100644 third_party/WebKit/public/web/WebV8ContextSnapshot.h create mode 100644 tools/v8_context_snapshot/BUILD.gn create mode 100644 tools/v8_context_snapshot/DEPS create mode 100644 tools/v8_context_snapshot/OWNERS create mode 100644 tools/v8_context_snapshot/run.py create mode 100644 tools/v8_context_snapshot/v8_context_snapshot_generator.cc diff --git a/build/config/features.gni b/build/config/features.gni index cd469c1361ecb7..21b3bbcc21c047 100644 --- a/build/config/features.gni +++ b/build/config/features.gni @@ -62,7 +62,8 @@ declare_args() { # Option controlling the use of GConf (the classic GNOME configuration # system). - use_gconf = is_linux && !is_chromeos && !is_chromecast + use_gconf = is_linux && !is_chromeos && !is_chromecast && + current_toolchain == default_toolchain use_gio = is_linux && !is_chromeos && !is_chromecast } diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn index 48efa8e9c4084a..e7cc499a583f39 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn @@ -117,6 +117,7 @@ if (!is_android && !is_mac) { group("chrome") { public_deps = [ ":chrome_initial", + "//tools/v8_context_snapshot:v8_context_snapshot", ] data_deps = [ ":chrome_initial", @@ -862,6 +863,7 @@ if (is_win) { bundle_data("chrome_framework_resources") { sources = [ "$root_out_dir/app_mode_loader.app", + "$root_out_dir/v8_context_snapshot.bin", # This image is used to badge the lock icon in the # authentication dialogs, such as those used for installation @@ -881,6 +883,7 @@ if (is_win) { public_deps = [ ":packed_resources", "//chrome/app_shim:app_mode_loader", + "//tools/v8_context_snapshot:v8_context_snapshot", ] if (is_chrome_branded) { diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index 9c8e3f494cbd3b..200bfcb1815b0f 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc @@ -54,6 +54,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" #include "content/public/common/sandbox_init.h" +#include "gin/v8_initializer.h" #include "media/base/media.h" #include "media/media_features.h" #include "ppapi/features/features.h" @@ -61,11 +62,6 @@ #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" -#if defined(V8_USE_EXTERNAL_STARTUP_DATA) && \ - !defined(CHROME_MULTIPLE_DLL_BROWSER) -#include "gin/v8_initializer.h" -#endif - #if defined(OS_WIN) #include #include @@ -178,6 +174,24 @@ void InitializeFieldTrialAndFeatureList( base::FeatureList::SetInstance(std::move(feature_list)); } +void LoadV8ContextSnapshotFile() { +#if defined(OS_POSIX) && !defined(OS_MACOSX) + base::FileDescriptorStore& file_descriptor_store = + base::FileDescriptorStore::GetInstance(); + base::MemoryMappedFile::Region region; + base::ScopedFD fd = file_descriptor_store.MaybeTakeFD( + kV8ContextSnapshotDataDescriptor, ®ion); + if (fd.is_valid()) { + gin::V8Initializer::LoadV8ContextSnapshotFromFD(fd.get(), region.offset, + region.size); + return; + } +#endif // OS +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + gin::V8Initializer::LoadV8ContextSnapshot(); +#endif // !CHROME_MULTIPLE_DLL_BROWSER +} + void InitializeV8IfNeeded( const base::CommandLine& command_line, const std::string& process_type) { @@ -194,24 +208,26 @@ void InitializeV8IfNeeded( if (v8_snapshot_fd.is_valid()) { gin::V8Initializer::LoadV8SnapshotFromFD(v8_snapshot_fd.get(), region.offset, region.size); - } else { - gin::V8Initializer::LoadV8Snapshot(); - } - base::ScopedFD v8_natives_fd = - file_descriptor_store.MaybeTakeFD(kV8NativesDataDescriptor, ®ion); - if (v8_natives_fd.is_valid()) { - gin::V8Initializer::LoadV8NativesFromFD(v8_natives_fd.get(), - region.offset, region.size); - } else { - gin::V8Initializer::LoadV8Natives(); - } -#else -#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + } else { gin::V8Initializer::LoadV8Snapshot(); + } + base::ScopedFD v8_natives_fd = + file_descriptor_store.MaybeTakeFD(kV8NativesDataDescriptor, ®ion); + if (v8_natives_fd.is_valid()) { + gin::V8Initializer::LoadV8NativesFromFD(v8_natives_fd.get(), region.offset, + region.size); + } else { gin::V8Initializer::LoadV8Natives(); + } +#else +#if !defined(CHROME_MULTIPLE_DLL_BROWSER) + gin::V8Initializer::LoadV8Snapshot(); + gin::V8Initializer::LoadV8Natives(); #endif // !CHROME_MULTIPLE_DLL_BROWSER #endif // OS_POSIX && !OS_MACOSX #endif // V8_USE_EXTERNAL_STARTUP_DATA + + LoadV8ContextSnapshotFile(); } } // namespace diff --git a/content/public/app/mojo/content_renderer_manifest.json b/content/public/app/mojo/content_renderer_manifest.json index de31d75f224319..a46429ea16a679 100644 --- a/content/public/app/mojo/content_renderer_manifest.json +++ b/content/public/app/mojo/content_renderer_manifest.json @@ -74,6 +74,12 @@ "v8_snapshot_64_data" : [{ "path": "assets/snapshot_blob_64.bin", "platform": "android" - }] + }], + "v8_context_snapshot_data" : [ + { + "path": "v8_context_snapshot.bin", + "platform": "linux" + } + ] } } diff --git a/content/public/common/content_descriptor_keys.cc b/content/public/common/content_descriptor_keys.cc index 25cf72e4b2c0b0..d2a6ba03d5290b 100644 --- a/content/public/common/content_descriptor_keys.cc +++ b/content/public/common/content_descriptor_keys.cc @@ -14,4 +14,6 @@ const char kV8Snapshot32DataDescriptor[] = "v8_snapshot_32_data"; const char kV8Snapshot64DataDescriptor[] = "v8_snapshot_64_data"; -} // namespace content \ No newline at end of file +const char kV8ContextSnapshotDataDescriptor[] = "v8_context_snapshot_data"; + +} // namespace content diff --git a/content/public/common/content_descriptor_keys.h b/content/public/common/content_descriptor_keys.h index dcc81104a35be3..80bccdf2cce5ea 100644 --- a/content/public/common/content_descriptor_keys.h +++ b/content/public/common/content_descriptor_keys.h @@ -18,6 +18,7 @@ extern const char kV8NativesDataDescriptor[]; extern const char kV8SnapshotDataDescriptor[]; extern const char kV8Snapshot32DataDescriptor[]; extern const char kV8Snapshot64DataDescriptor[]; +extern const char kV8ContextSnapshotDataDescriptor[]; } // namespace content diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 57d9fd5f278b01..8d00af8a83823f 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn @@ -544,6 +544,14 @@ if (is_android) { ":pak", ] + public_deps = [ + "//tools/v8_context_snapshot:v8_context_snapshot", + ] + + data = [ + "$root_out_dir/v8_context_snapshot.bin", + ] + if (is_win) { deps += [ "//sandbox" ] @@ -608,11 +616,13 @@ if (is_mac) { bundle_data("content_shell_framework_resources") { sources = [ "$root_out_dir/content_shell.pak", + "$root_out_dir/v8_context_snapshot.bin", "resources/missingImage.png", ] public_deps = [ ":pak", + "//tools/v8_context_snapshot:v8_context_snapshot", ] if (icu_use_data_file) { diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn index 38d9f84d3ea221..4f0111f3107f2a 100644 --- a/extensions/shell/BUILD.gn +++ b/extensions/shell/BUILD.gn @@ -336,10 +336,12 @@ if (is_mac) { sources = [ "$root_gen_dir/extensions/shell/app_shell_resources.pak", "$root_out_dir/extensions_shell_and_test.pak", + "$root_out_dir/v8_context_snapshot.bin", ] public_deps = [ ":resources_grit", "//extensions:shell_and_test_pak", + "//tools/v8_context_snapshot:v8_context_snapshot", ] if (icu_use_data_file) { sources += [ "$root_out_dir/icudtl.dat" ] diff --git a/gin/isolate_holder.cc b/gin/isolate_holder.cc index d42607d86ddea5..6df4689e0ec516 100644 --- a/gin/isolate_holder.cc +++ b/gin/isolate_holder.cc @@ -15,6 +15,7 @@ #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" #include "base/sys_info.h" +#include "build/build_config.h" #include "gin/debug_impl.h" #include "gin/function_template.h" #include "gin/per_isolate_data.h" @@ -35,15 +36,22 @@ IsolateHolder::IsolateHolder( IsolateHolder::IsolateHolder( scoped_refptr task_runner, AccessMode access_mode) - : IsolateHolder(std::move(task_runner), access_mode, kAllowAtomicsWait) {} + : IsolateHolder(std::move(task_runner), + access_mode, + kAllowAtomicsWait, + nullptr, + nullptr) {} IsolateHolder::IsolateHolder( scoped_refptr task_runner, AccessMode access_mode, - AllowAtomicsWaitMode atomics_wait_mode) + AllowAtomicsWaitMode atomics_wait_mode, + intptr_t* reference, + v8::StartupData* startup_data) : access_mode_(access_mode) { v8::ArrayBuffer::Allocator* allocator = g_array_buffer_allocator; CHECK(allocator) << "You need to invoke gin::IsolateHolder::Initialize first"; + v8::Isolate::CreateParams params; params.entry_hook = DebugImpl::GetFunctionEntryHook(); params.code_event_handler = DebugImpl::GetJitCodeEventHandler(); @@ -51,45 +59,28 @@ IsolateHolder::IsolateHolder( base::SysInfo::AmountOfVirtualMemory()); params.array_buffer_allocator = allocator; params.allow_atomics_wait = atomics_wait_mode == kAllowAtomicsWait; - isolate_ = v8::Isolate::New(params); - isolate_data_.reset( - new PerIsolateData(isolate_, allocator, access_mode, task_runner)); - isolate_memory_dump_provider_.reset(new V8IsolateMemoryDumpProvider(this)); -#if defined(OS_WIN) - { - void* code_range; - size_t size; - isolate_->GetCodeRange(&code_range, &size); - Debug::CodeRangeCreatedCallback callback = - DebugImpl::GetCodeRangeCreatedCallback(); - if (code_range && size && callback) - callback(code_range, size); + params.external_references = reference; + + if (startup_data) { + CHECK(reference); + V8Initializer::GetV8ContextSnapshotData(&startup_data->data, + &startup_data->raw_size); + if (startup_data->data) { + params.snapshot_blob = startup_data; + } } -#endif + isolate_ = v8::Isolate::New(params); + + SetUp(std::move(task_runner)); } IsolateHolder::IsolateHolder(intptr_t* reference_table, v8::StartupData* existing_blob) : snapshot_creator_( new v8::SnapshotCreator(reference_table, existing_blob)), - access_mode_(kSingleThread) { - v8::ArrayBuffer::Allocator* allocator = g_array_buffer_allocator; - CHECK(allocator) << "You need to invoke gin::IsolateHolder::Initialize first"; - isolate_ = snapshot_creator_->GetIsolate(); - isolate_data_.reset( - new PerIsolateData(isolate_, allocator, access_mode_, nullptr)); - isolate_memory_dump_provider_.reset(new V8IsolateMemoryDumpProvider(this)); -#if defined(OS_WIN) - { - void* code_range; - size_t size; - isolate_->GetCodeRange(&code_range, &size); - Debug::CodeRangeCreatedCallback callback = - DebugImpl::GetCodeRangeCreatedCallback(); - if (code_range && size && callback) - callback(code_range, size); - } -#endif + isolate_(snapshot_creator_->GetIsolate()), + access_mode_(AccessMode::kSingleThread) { + SetUp(nullptr); } IsolateHolder::~IsolateHolder() { @@ -109,7 +100,7 @@ IsolateHolder::~IsolateHolder() { isolate_memory_dump_provider_.reset(); isolate_data_.reset(); isolate_->Dispose(); - isolate_ = NULL; + isolate_ = nullptr; } // static @@ -139,4 +130,24 @@ void IsolateHolder::EnableIdleTasks( isolate_data_->EnableIdleTasks(std::move(idle_task_runner)); } +void IsolateHolder::SetUp( + scoped_refptr task_runner) { + v8::ArrayBuffer::Allocator* allocator = g_array_buffer_allocator; + CHECK(allocator) << "You need to invoke gin::IsolateHolder::Initialize first"; + isolate_data_.reset( + new PerIsolateData(isolate_, allocator, access_mode_, task_runner)); + isolate_memory_dump_provider_.reset(new V8IsolateMemoryDumpProvider(this)); +#if defined(OS_WIN) + { + void* code_range; + size_t size; + isolate_->GetCodeRange(&code_range, &size); + Debug::CodeRangeCreatedCallback callback = + DebugImpl::GetCodeRangeCreatedCallback(); + if (code_range && size && callback) + callback(code_range, size); + } +#endif +} + } // namespace gin diff --git a/gin/public/isolate_holder.h b/gin/public/isolate_holder.h index e2846213865721..fb7ffe0880f0e3 100644 --- a/gin/public/isolate_holder.h +++ b/gin/public/isolate_holder.h @@ -59,7 +59,9 @@ class GIN_EXPORT IsolateHolder { AccessMode access_mode); IsolateHolder(scoped_refptr task_runner, AccessMode access_mode, - AllowAtomicsWaitMode atomics_wait_mode); + AllowAtomicsWaitMode atomics_wait_mode, + intptr_t* reference_table, + v8::StartupData* startup_data); // This constructor is to create V8 snapshot for Blink. // Note this constructor calls isolate->Enter() internally. @@ -110,11 +112,13 @@ class GIN_EXPORT IsolateHolder { } private: + void SetUp(scoped_refptr task_runner); + + std::unique_ptr snapshot_creator_; v8::Isolate* isolate_; std::unique_ptr isolate_data_; std::unique_ptr task_observer_; std::unique_ptr isolate_memory_dump_provider_; - std::unique_ptr snapshot_creator_; AccessMode access_mode_; DISALLOW_COPY_AND_ASSIGN(IsolateHolder); diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc index 56011d9ff5be2e..ca0e3efd3b47b9 100644 --- a/gin/v8_initializer.cc +++ b/gin/v8_initializer.cc @@ -18,20 +18,20 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/path_service.h" #include "base/rand_util.h" #include "base/strings/sys_string_conversions.h" #include "base/sys_info.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" +#include "build/build_config.h" #if defined(V8_USE_EXTERNAL_STARTUP_DATA) #if defined(OS_ANDROID) #include "base/android/apk_assets.h" -#endif -#if defined(OS_MACOSX) +#elif defined(OS_MACOSX) #include "base/mac/foundation_util.h" -#endif // OS_MACOSX -#include "base/path_service.h" +#endif #endif // V8_USE_EXTERNAL_STARTUP_DATA namespace gin { @@ -41,16 +41,17 @@ namespace { // None of these globals are ever freed nor closed. base::MemoryMappedFile* g_mapped_natives = nullptr; base::MemoryMappedFile* g_mapped_snapshot = nullptr; +base::MemoryMappedFile* g_mapped_v8_context_snapshot = nullptr; -#if defined(V8_USE_EXTERNAL_STARTUP_DATA) +const char kV8ContextSnapshotFileName[] = "v8_context_snapshot.bin"; // File handles intentionally never closed. Not using File here because its // Windows implementation guards against two instances owning the same // PlatformFile (which we allow since we know it is never freed). -typedef std::map> - OpenedFileMap; -static base::LazyInstance::Leaky g_opened_files = +using OpenedFileMap = + std::map>; +base::LazyInstance::Leaky g_opened_files = LAZY_INSTANCE_INITIALIZER; OpenedFileMap::mapped_type& GetOpenedFile(const char* file) { @@ -62,6 +63,8 @@ OpenedFileMap::mapped_type& GetOpenedFile(const char* file) { return opened_files[file]; } +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) + const char kNativesFileName[] = "natives_blob.bin"; #if defined(OS_ANDROID) @@ -78,6 +81,8 @@ const char kSnapshotFileName32[] = "snapshot_blob_32.bin"; const char kSnapshotFileName[] = "snapshot_blob.bin"; #endif // defined(OS_ANDROID) +#endif // defined(V8_USE_EXTERNAL_STATUP_DATA) + void GetV8FilePath(const char* file_name, base::FilePath* path_out) { #if !defined(OS_MACOSX) base::FilePath data_path; @@ -97,12 +102,11 @@ void GetV8FilePath(const char* file_name, base::FilePath* path_out) { base::SysUTF8ToCFStringRef(file_name)); *path_out = base::mac::PathForFrameworkBundleResource(natives_file_name); #endif // !defined(OS_MACOSX) - DCHECK(!path_out->empty()); } -static bool MapV8File(base::PlatformFile platform_file, - base::MemoryMappedFile::Region region, - base::MemoryMappedFile** mmapped_file_out) { +bool MapV8File(base::PlatformFile platform_file, + base::MemoryMappedFile::Region region, + base::MemoryMappedFile** mmapped_file_out) { DCHECK(*mmapped_file_out == NULL); std::unique_ptr mmapped_file( new base::MemoryMappedFile()); @@ -180,8 +184,7 @@ base::PlatformFile OpenV8File(const char* file_name, return file.TakePlatformFile(); } -static const OpenedFileMap::mapped_type OpenFileIfNecessary( - const char* file_name) { +const OpenedFileMap::mapped_type OpenFileIfNecessary(const char* file_name) { OpenedFileMap::mapped_type& opened = GetOpenedFile(file_name); if (opened.first == base::kInvalidPlatformFile) { opened.first = OpenV8File(file_name, &opened.second); @@ -189,19 +192,11 @@ static const OpenedFileMap::mapped_type OpenFileIfNecessary( return opened; } -#endif // V8_USE_EXTERNAL_STARTUP_DATA - bool GenerateEntropy(unsigned char* buffer, size_t amount) { base::RandBytes(buffer, amount); return true; } -} // namespace - -#if defined(V8_USE_EXTERNAL_STARTUP_DATA) - -namespace { - enum LoadV8FileResult { V8_LOAD_SUCCESS = 0, V8_LOAD_FAILED_OPEN, @@ -210,9 +205,8 @@ enum LoadV8FileResult { V8_LOAD_MAX_VALUE }; -static LoadV8FileResult MapOpenedFile( - const OpenedFileMap::mapped_type& file_region, - base::MemoryMappedFile** mmapped_file_out) { +LoadV8FileResult MapOpenedFile(const OpenedFileMap::mapped_type& file_region, + base::MemoryMappedFile** mmapped_file_out) { if (file_region.first == base::kInvalidPlatformFile) return V8_LOAD_FAILED_OPEN; if (!MapV8File(file_region.first, file_region.second, mmapped_file_out)) @@ -222,6 +216,8 @@ static LoadV8FileResult MapOpenedFile( } // namespace +#if defined(V8_USE_EXTERNAL_STARTUP_DATA) + // static void V8Initializer::LoadV8Snapshot() { if (g_mapped_snapshot) @@ -370,7 +366,7 @@ void V8Initializer::Initialize(IsolateHolder::ScriptMode mode, natives.raw_size = static_cast(g_mapped_natives->length()); v8::V8::SetNativesDataBlob(&natives); - if (g_mapped_snapshot != NULL) { + if (g_mapped_snapshot) { v8::StartupData snapshot; snapshot.data = reinterpret_cast(g_mapped_snapshot->data()); snapshot.raw_size = static_cast(g_mapped_snapshot->length()); @@ -406,4 +402,51 @@ void V8Initializer::GetV8ExternalSnapshotData(const char** natives_data_out, } } +// static +void V8Initializer::LoadV8ContextSnapshot() { + if (g_mapped_v8_context_snapshot) + return; + + OpenFileIfNecessary(kV8ContextSnapshotFileName); + MapOpenedFile(GetOpenedFile(kV8ContextSnapshotFileName), + &g_mapped_v8_context_snapshot); + + // TODO(peria): Check if the snapshot file is loaded successfully. +} + +// static +void V8Initializer::LoadV8ContextSnapshotFromFD(base::PlatformFile snapshot_pf, + int64_t snapshot_offset, + int64_t snapshot_size) { + if (g_mapped_v8_context_snapshot) + return; + CHECK_NE(base::kInvalidPlatformFile, snapshot_pf); + + base::MemoryMappedFile::Region snapshot_region = + base::MemoryMappedFile::Region::kWholeFile; + if (snapshot_size != 0 || snapshot_offset != 0) { + snapshot_region.offset = snapshot_offset; + snapshot_region.size = snapshot_size; + } + + if (MapV8File(snapshot_pf, snapshot_region, &g_mapped_v8_context_snapshot)) { + g_opened_files.Get()[kV8ContextSnapshotFileName] = + std::make_pair(snapshot_pf, snapshot_region); + } +} + +// static +void V8Initializer::GetV8ContextSnapshotData(const char** snapshot_data_out, + int* snapshot_size_out) { + if (g_mapped_v8_context_snapshot) { + *snapshot_data_out = + reinterpret_cast(g_mapped_v8_context_snapshot->data()); + *snapshot_size_out = + static_cast(g_mapped_v8_context_snapshot->length()); + } else { + *snapshot_data_out = nullptr; + *snapshot_size_out = 0; + } +} + } // namespace gin diff --git a/gin/v8_initializer.h b/gin/v8_initializer.h index 7d16c3eb6d940e..8601b15238e126 100644 --- a/gin/v8_initializer.h +++ b/gin/v8_initializer.h @@ -76,6 +76,20 @@ class GIN_EXPORT V8Initializer { #endif #endif // V8_USE_EXTERNAL_STARTUP_DATA + + // Load V8 context snapshot from user provided platform file descriptors. + // Other details are same with LoadV8SnapshotFromFD. + static void LoadV8ContextSnapshotFromFD(base::PlatformFile snapshot_fd, + int64_t snapshot_offset, + int64_t snapshot_size); + + // Load V8 context snapshot from default resources, if they are available. + static void LoadV8ContextSnapshot(); + + // Get address and size information for currently loaded V8 context snapshot. + // If no snapshot is loaded, the return values are nullptr and 0. + static void GetV8ContextSnapshotData(const char** snapshot_data_out, + int* snapshot_size_out); }; } // namespace gin diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-2x-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-2x-expected.txt index 1d4cce5d164f9c..4ca2b4cba4bf53 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-2x-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-2x-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=320 @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 192dpi @media device-pixel-ratio = 2 window.devicePixelRatio = 2 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-expected.txt index 03ba35c055b87e..55b8a7e6ffa0eb 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=320 @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-only-viewport-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-only-viewport-expected.txt index 7beb25aca2c2fe..78a1df56553d33 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-only-viewport-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-320-only-viewport-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=320 @media orientation = landscape -window.orientation +window.orientation = 0 Widths: doc.docElem.clientWidth = 320px doc.docElem.offsetWidth = 320px diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-2x-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-2x-expected.txt index b84aa8c14f3bd3..85d9b5ba21a85b 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-2x-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-2x-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=980 @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 192dpi @media device-pixel-ratio = 2 window.devicePixelRatio = 2 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-expected.txt index bf60f7d32f0872..a4049a3fb4b2fd 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=980 @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-only-viewport-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-only-viewport-expected.txt index f2017f0ccb4080..62484f00180528 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-only-viewport-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-980-only-viewport-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=980 @media orientation = landscape -window.orientation +window.orientation = 0 Widths: doc.docElem.clientWidth = 980px doc.docElem.offsetWidth = 980px diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-controls-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-controls-expected.txt index f30597ca14eeaf..53d765e64f9c1b 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-controls-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-controls-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?none @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-2x-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-2x-expected.txt index 7af4b249e5602f..99cfdc92b2aa36 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-2x-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-2x-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=dw @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 192dpi @media device-pixel-ratio = 2 window.devicePixelRatio = 2 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-expected.txt index 69b3f42ba80ba0..26cba55f18d78b 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-dw-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=dw @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-initial-scale-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-initial-scale-expected.txt index 401808d2a5f78b..275c6e3f0c8021 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-initial-scale-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-initial-scale-expected.txt @@ -11,7 +11,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=980 @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 288dpi @media device-pixel-ratio = 3 window.devicePixelRatio = 3 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-insets-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-insets-expected.txt index f357336d3a8ff5..c758332721086d 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-insets-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-insets-expected.txt @@ -6,7 +6,7 @@ window.screenX = 10px window.screenY = 20px Viewport: = ?none @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-2x-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-2x-expected.txt index dfd32a25ee3c1f..e4adbe16ad13a2 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-2x-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-2x-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?none @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 192dpi @media device-pixel-ratio = 2 window.devicePixelRatio = 2 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-expected.txt index 06d447ecaa0665..ca8e4cba01e8af 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-none-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?none @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-restore-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-restore-expected.txt index 66e8ce45195bec..71012a0cb7899b 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-restore-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-restore-expected.txt @@ -6,7 +6,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=320 @media orientation = landscape -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-dw-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-dw-expected.txt index 9df155a658ebe5..b4e2ceac28e543 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-dw-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-dw-expected.txt @@ -9,7 +9,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?w=dw @media orientation = portrait -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-expected.txt index 8540d74a950788..2222e52e315fb1 100644 --- a/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-expected.txt +++ b/third_party/WebKit/LayoutTests/inspector-protocol/emulation/device-emulation-small-expected.txt @@ -9,7 +9,7 @@ window.screenX = 0px window.screenY = 0px Viewport: = ?none @media orientation = portrait -window.orientation +window.orientation = 0 @media resolution = 96dpi @media device-pixel-ratio = 1 window.devicePixelRatio = 1 diff --git a/third_party/WebKit/Source/bindings/bindings.gni b/third_party/WebKit/Source/bindings/bindings.gni index c30bf1ff40164f..ac9de4a4dcee5e 100644 --- a/third_party/WebKit/Source/bindings/bindings.gni +++ b/third_party/WebKit/Source/bindings/bindings.gni @@ -148,6 +148,8 @@ bindings_core_v8_files = "core/v8/V8PersistentValueVector.h", "core/v8/V8ScriptRunner.cpp", "core/v8/V8ScriptRunner.h", + "core/v8/V8ContextSnapshot.cpp", + "core/v8/V8ContextSnapshot.h", "core/v8/V8StringResource.h", "core/v8/V8V0CustomElementLifecycleCallbacks.cpp", "core/v8/V8V0CustomElementLifecycleCallbacks.h", diff --git a/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp b/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp index 2b02bcf1168011..acb6b023af1f4c 100644 --- a/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/LocalWindowProxy.cpp @@ -33,6 +33,7 @@ #include "bindings/core/v8/ScriptController.h" #include "bindings/core/v8/ToV8ForCore.h" #include "bindings/core/v8/V8BindingForCore.h" +#include "bindings/core/v8/V8ContextSnapshot.h" #include "bindings/core/v8/V8DOMActivityLogger.h" #include "bindings/core/v8/V8GCForContextDispose.h" #include "bindings/core/v8/V8HTMLDocument.h" @@ -114,7 +115,7 @@ void LocalWindowProxy::DisposeContext(Lifecycle next_status, } void LocalWindowProxy::Initialize() { - TRACE_EVENT1("v8", "LocalWindowProxy::initialize", "isMainWindow", + TRACE_EVENT1("v8", "LocalWindowProxy::Initialize", "IsMainFrame", GetFrame()->IsMainFrame()); SCOPED_BLINK_UMA_HISTOGRAM_TIMER( GetFrame()->IsMainFrame() @@ -135,6 +136,8 @@ void LocalWindowProxy::Initialize() { } SetupWindowPrototypeChain(); + V8ContextSnapshot::InstallRuntimeEnabledFeatures(context, + GetFrame()->GetDocument()); SecurityOrigin* origin = 0; if (world_->IsMainWorld()) { @@ -155,31 +158,32 @@ void LocalWindowProxy::Initialize() { SetSecurityToken(origin); } - MainThreadDebugger::Instance()->ContextCreated(script_state_.Get(), - GetFrame(), origin); - GetFrame()->Client()->DidCreateScriptContext(context, world_->GetWorldId()); - - InstallConditionalFeaturesOnGlobal(&V8Window::wrapperTypeInfo, - script_state_.Get()); - - if (world_->IsMainWorld()) { - // For the main world, install any remaining conditional bindings (i.e. for - // origin trials, which do not apply to extensions). Some conditional - // bindings cannot be enabled until the execution context is available - // (e.g. parsing the document, inspecting HTTP headers). - InstallConditionalFeatures(&V8Window::wrapperTypeInfo, script_state_.Get(), - v8::Local(), - v8::Local()); - GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld(); + { + TRACE_EVENT1("v8", "ContextCreatedNotification", "IsMainFrame", + GetFrame()->IsMainFrame()); + MainThreadDebugger::Instance()->ContextCreated(script_state_.Get(), + GetFrame(), origin); + GetFrame()->Client()->DidCreateScriptContext(context, world_->GetWorldId()); + + InstallConditionalFeaturesOnGlobal(&V8Window::wrapperTypeInfo, + script_state_.Get()); + + if (world_->IsMainWorld()) { + // For the main world, install any remaining conditional bindings (i.e. + // for origin trials, which do not apply to extensions). Some conditional + // bindings cannot be enabled until the execution context is available + // (e.g. parsing the document, inspecting HTTP headers). + InstallConditionalFeatures(&V8Window::wrapperTypeInfo, + script_state_.Get(), v8::Local(), + v8::Local()); + GetFrame()->Loader().DispatchDidClearWindowObjectInMainWorld(); + } } } void LocalWindowProxy::CreateContext() { - // Create a new v8::Context with the window object as the global object - // (aka the inner global). Reuse the outer global proxy if it already exists. - v8::Local global_template = - V8Window::domTemplate(GetIsolate(), *world_)->InstanceTemplate(); - CHECK(!global_template.IsEmpty()); + TRACE_EVENT1("v8", "LocalWindowProxy::CreateContext", "IsMainFrame", + GetFrame()->IsMainFrame()); Vector extension_names; // Dynamically tell v8 about our extensions now. @@ -194,11 +198,25 @@ void LocalWindowProxy::CreateContext() { v8::Local context; { + v8::Isolate* isolate = GetIsolate(); + Document* document = GetFrame()->GetDocument(); V8PerIsolateData::UseCounterDisabledScope use_counter_disabled( - V8PerIsolateData::From(GetIsolate())); - context = - v8::Context::New(GetIsolate(), &extension_configuration, - global_template, global_proxy_.NewLocal(GetIsolate())); + V8PerIsolateData::From(isolate)); + + v8::Local global_proxy = global_proxy_.NewLocal(isolate); + context = V8ContextSnapshot::CreateContextFromSnapshot( + isolate, World(), &extension_configuration, global_proxy, document); + + // Even if we enable V8 context snapshot feature, we may hit this branch + // in some cases, e.g. loading XML files. + if (context.IsEmpty()) { + v8::Local global_template = + V8Window::domTemplate(isolate, *world_)->InstanceTemplate(); + CHECK(!global_template.IsEmpty()); + context = v8::Context::New(isolate, &extension_configuration, + global_template, global_proxy); + VLOG(1) << "A context is created NOT from snapshot"; + } } CHECK(!context.IsEmpty()); @@ -215,6 +233,9 @@ void LocalWindowProxy::CreateContext() { } void LocalWindowProxy::SetupWindowPrototypeChain() { + TRACE_EVENT1("v8", "LocalWindowProxy::SetupWindowPrototypeChain", + "IsMainFrame", GetFrame()->IsMainFrame()); + // Associate the window wrapper object and its prototype chain with the // corresponding native DOMWindow object. DOMWindow* window = GetFrame()->DomWindow(); @@ -259,12 +280,15 @@ void LocalWindowProxy::SetupWindowPrototypeChain() { void LocalWindowProxy::UpdateDocumentProperty() { DCHECK(world_->IsMainWorld()); + TRACE_EVENT1("v8", "LocalWindowProxy::UpdateDocumentProperty", "IsMainFrame", + GetFrame()->IsMainFrame()); ScriptState::Scope scope(script_state_.Get()); v8::Local context = script_state_->GetContext(); v8::Local document_wrapper = ToV8(GetFrame()->GetDocument(), context->Global(), GetIsolate()); DCHECK(document_wrapper->IsObject()); + // Update the cached accessor for window.document. CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(GetIsolate()) .Set(context->Global(), document_wrapper)); @@ -326,8 +350,8 @@ void LocalWindowProxy::SetSecurityToken(SecurityOrigin* origin) { String frame_security_token = frame_security_origin->ToString(); // We need to check the return value of domainWasSetInDOM() on the // frame's SecurityOrigin because, if that's the case, only - // SecurityOrigin::m_domain would have been modified. - // m_domain is not used by SecurityOrigin::toString(), so we would end + // SecurityOrigin::domain_ would have been modified. + // domain_ is not used by SecurityOrigin::toString(), so we would end // up generating the same token that was already set. if (frame_security_origin->DomainWasSetInDOM() || frame_security_token.IsEmpty() || frame_security_token == "null") { diff --git a/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp b/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp new file mode 100644 index 00000000000000..f6ffec94779bca --- /dev/null +++ b/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.cpp @@ -0,0 +1,501 @@ +// Copyright 2017 The Chromium 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 "bindings/core/v8/V8ContextSnapshot.h" + +#include +#include + +#include "bindings/core/v8/GeneratedCodeHelper.h" +#include "bindings/core/v8/V8Document.h" +#include "bindings/core/v8/V8EventTarget.h" +#include "bindings/core/v8/V8HTMLDocument.h" +#include "bindings/core/v8/V8Initializer.h" +#include "bindings/core/v8/V8Node.h" +#include "bindings/core/v8/V8Window.h" +#include "platform/bindings/DOMWrapperWorld.h" +#include "platform/bindings/V8ObjectConstructor.h" +#include "platform/bindings/V8PerIsolateData.h" +#include "platform/bindings/V8PrivateProperty.h" +#include "platform/instrumentation/tracing/TraceEvent.h" +#include "v8/include/v8.h" + +namespace blink { + +namespace { + +intptr_t* g_v8_context_snapshot_reference_table = nullptr; + +// TODO(peria): This method is almost a copy of +// V8PerContext::ConstructorForTypeSlowCase(), so merge with it. +v8::Local ConstructPlainType(v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::Local context, + const WrapperTypeInfo* type) { + v8::Context::Scope scope(context); + // We shouldn't reach this point for the types that are implemented in v8 such + // as typed arrays and hence don't have domTemplateFunction. + DCHECK(type->dom_template_function); + v8::Local interface_template = + type->domTemplate(isolate, world); + // Getting the function might fail if we're running out of stack or memory. + v8::Local interface_object = + interface_template->GetFunction(context).ToLocalChecked(); + + if (type->parent_class) { + v8::Local prototype_template = + ConstructPlainType(isolate, world, context, type->parent_class); + CHECK(interface_object->SetPrototype(context, prototype_template) + .ToChecked()); + } + + v8::Local prototype_value = + interface_object->Get(context, V8AtomicString(isolate, "prototype")) + .ToLocalChecked(); + CHECK(prototype_value->IsObject()); + v8::Local prototype_object = prototype_value.As(); + if (prototype_object->InternalFieldCount() == + kV8PrototypeInternalFieldcount && + type->wrapper_type_prototype == + WrapperTypeInfo::kWrapperTypeObjectPrototype) { + prototype_object->SetAlignedPointerInInternalField( + kV8PrototypeTypeIndex, const_cast(type)); + } + type->PreparePrototypeAndInterfaceObject( + context, world, prototype_object, interface_object, interface_template); + + return interface_object; +} + +// TODO(peria): This method is almost a copy of +// V8PerContext::CreateWrapperFromCacheSlowCase(), so merge with it. +v8::Local CreatePlainWrapper(v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::Local context, + const WrapperTypeInfo* type) { + CHECK(V8HTMLDocument::wrapperTypeInfo.Equals(type)); + + v8::Context::Scope scope(context); + v8::Local interface_object = + ConstructPlainType(isolate, world, context, type); + CHECK(!interface_object.IsEmpty()); + v8::Local instance_template = + V8ObjectConstructor::NewInstance(isolate, interface_object) + .ToLocalChecked(); + v8::Local wrapper = instance_template->Clone(); + wrapper->SetAlignedPointerInInternalField(kV8DOMWrapperTypeIndex, + const_cast(type)); + return wrapper; +} + +int GetSnapshotIndexForWorld(const DOMWrapperWorld& world) { + return world.IsMainWorld() ? 0 : 1; +} + +// Interface templates of those classes are stored in a snapshot without any +// runtime enabled features, so we have to install runtime enabled features on +// them after instantiation. +struct SnapshotInterface { + const WrapperTypeInfo* wrapper_type_info; + InstallRuntimeEnabledFeaturesOnTemplateFunction install_function; +}; +SnapshotInterface g_snapshot_interfaces[] = { + {&V8Window::wrapperTypeInfo, + V8Window::InstallRuntimeEnabledFeaturesOnTemplate}, + {&V8HTMLDocument::wrapperTypeInfo, + V8HTMLDocument::InstallRuntimeEnabledFeaturesOnTemplate}, + {&V8EventTarget::wrapperTypeInfo, + V8EventTarget::InstallRuntimeEnabledFeaturesOnTemplate}, + {&V8Node::wrapperTypeInfo, V8Node::InstallRuntimeEnabledFeaturesOnTemplate}, + {&V8Document::wrapperTypeInfo, + V8Document::InstallRuntimeEnabledFeaturesOnTemplate}, +}; +constexpr size_t kSnapshotInterfaceSize = + WTF_ARRAY_LENGTH(g_snapshot_interfaces); + +enum class InternalFieldType : uint8_t { + kNone, + kNodeType, + kDocumentType, + kHTMLDocumentType, + kHTMLDocumentObject, +}; + +const WrapperTypeInfo* FieldTypeToWrapperTypeInfo(InternalFieldType type) { + switch (type) { + case InternalFieldType::kNone: + NOTREACHED(); + break; + case InternalFieldType::kNodeType: + return &V8Node::wrapperTypeInfo; + case InternalFieldType::kDocumentType: + return &V8Document::wrapperTypeInfo; + case InternalFieldType::kHTMLDocumentType: + return &V8HTMLDocument::wrapperTypeInfo; + case InternalFieldType::kHTMLDocumentObject: + return &V8HTMLDocument::wrapperTypeInfo; + } + NOTREACHED(); + return nullptr; +} + +struct DataForDeserializer { + STACK_ALLOCATED(); + Member document; +}; + +int CountExternalReferenceEntries() { + if (!g_v8_context_snapshot_reference_table) + return 0; + + int count = 0; + for (intptr_t* p = g_v8_context_snapshot_reference_table; *p; ++p) + ++count; + return count; +} + +} // namespace + +v8::Local V8ContextSnapshot::CreateContextFromSnapshot( + v8::Isolate* isolate, + const DOMWrapperWorld& world, + v8::ExtensionConfiguration* extension_configuration, + v8::Local global_proxy, + Document* document) { + if (!CanCreateContextFromSnapshot(isolate, world, document)) { + return v8::Local(); + } + + const int index = GetSnapshotIndexForWorld(world); + DataForDeserializer data{document}; + v8::DeserializeInternalFieldsCallback callback = + v8::DeserializeInternalFieldsCallback(&DeserializeInternalField, &data); + v8::Local context = + v8::Context::FromSnapshot(isolate, index, callback, + extension_configuration, global_proxy) + .ToLocalChecked(); + VLOG(1) << "A context is created from snapshot for " + << (world.IsMainWorld() ? "" : "non-") << "main world"; + + return context; +} + +void V8ContextSnapshot::InstallRuntimeEnabledFeatures( + v8::Local context, + Document* document) { + ScriptState* script_state = ScriptState::From(context); + v8::Isolate* isolate = script_state->GetIsolate(); + const DOMWrapperWorld& world = script_state->World(); + if (!CanCreateContextFromSnapshot(isolate, world, document)) { + return; + } + + TRACE_EVENT1("v8", "V8ContextSnapshot::InstallRuntimeEnabled", "IsMainFrame", + world.IsMainWorld()); + + v8::Local prototype_str = V8AtomicString(isolate, "prototype"); + V8PerContextData* data = script_state->PerContextData(); + + v8::Local global_proxy = context->Global(); + { + v8::Local window_wrapper = + global_proxy->GetPrototype().As(); + const WrapperTypeInfo* type = &V8Window::wrapperTypeInfo; + v8::Local interface = data->ConstructorForType(type); + v8::Local prototype = interface->Get(context, prototype_str) + .ToLocalChecked() + .As(); + V8Window::install_runtime_enabled_features_function_( + isolate, world, window_wrapper, prototype, interface); + } + { + const WrapperTypeInfo* type = &V8EventTarget::wrapperTypeInfo; + v8::Local interface = data->ConstructorForType(type); + v8::Local prototype = interface->Get(context, prototype_str) + .ToLocalChecked() + .As(); + V8EventTarget::InstallRuntimeEnabledFeatures( + isolate, world, v8::Local(), prototype, interface); + } + + if (!world.IsMainWorld()) { + return; + } + + // The below code handles window.document on the main world. + { + CHECK(document); + DCHECK(document->IsHTMLDocument()); + CHECK(document->ContainsWrapper()); + v8::Local document_wrapper = + ToV8(document, global_proxy, isolate).As(); + const WrapperTypeInfo* type = &V8HTMLDocument::wrapperTypeInfo; + v8::Local interface = data->ConstructorForType(type); + v8::Local prototype = interface->Get(context, prototype_str) + .ToLocalChecked() + .As(); + V8HTMLDocument::InstallRuntimeEnabledFeatures( + isolate, world, document_wrapper, prototype, interface); + } + { + const WrapperTypeInfo* type = &V8Document::wrapperTypeInfo; + v8::Local interface = data->ConstructorForType(type); + v8::Local prototype = interface->Get(context, prototype_str) + .ToLocalChecked() + .As(); + V8Document::InstallRuntimeEnabledFeatures( + isolate, world, v8::Local(), prototype, interface); + } + { + const WrapperTypeInfo* type = &V8Node::wrapperTypeInfo; + v8::Local interface = data->ConstructorForType(type); + v8::Local prototype = interface->Get(context, prototype_str) + .ToLocalChecked() + .As(); + V8Node::InstallRuntimeEnabledFeatures( + isolate, world, v8::Local(), prototype, interface); + } +} + +void V8ContextSnapshot::EnsureInterfaceTemplates(v8::Isolate* isolate) { + if (V8PerIsolateData::From(isolate)->GetV8ContextSnapshotMode() != + V8PerIsolateData::V8ContextSnapshotMode::kUseSnapshot) { + return; + } + + v8::HandleScope handle_scope(isolate); + SnapshotInterface& snapshot_window = g_snapshot_interfaces[0]; + DCHECK(V8Window::wrapperTypeInfo.Equals(snapshot_window.wrapper_type_info)); + // Update the install function for V8Window to work for partial interfaces. + snapshot_window.install_function = + V8Window::install_runtime_enabled_features_on_template_function_; + + EnsureInterfaceTemplatesForWorld(isolate, DOMWrapperWorld::MainWorld()); + // Any world types other than |kMain| are acceptable for this. + RefPtr isolated_world = DOMWrapperWorld::Create( + isolate, DOMWrapperWorld::WorldType::kForV8ContextSnapshotNonMain); + EnsureInterfaceTemplatesForWorld(isolate, *isolated_world); +} + +void V8ContextSnapshot::SetReferenceTable(intptr_t* table) { + DCHECK(!g_v8_context_snapshot_reference_table); + g_v8_context_snapshot_reference_table = table; +} + +intptr_t* V8ContextSnapshot::GetReferenceTable() { + return g_v8_context_snapshot_reference_table; +} + +v8::StartupData V8ContextSnapshot::TakeSnapshot() { + DCHECK_EQ(V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate()) + ->GetV8ContextSnapshotMode(), + V8PerIsolateData::V8ContextSnapshotMode::kTakeSnapshot); + + v8::SnapshotCreator* creator = + V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate()) + ->GetSnapshotCreator(); + v8::Isolate* isolate = creator->GetIsolate(); + CHECK_EQ(isolate, v8::Isolate::GetCurrent()); + + VLOG(1) << "External reference table has " << CountExternalReferenceEntries() + << " entries."; + + // Disable all runtime enabled features + RuntimeEnabledFeatures::SetStableFeaturesEnabled(false); + RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(false); + RuntimeEnabledFeatures::SetTestFeaturesEnabled(false); + + { + v8::HandleScope handleScope(isolate); + creator->SetDefaultContext(v8::Context::New(isolate)); + + TakeSnapshotForWorld(creator, DOMWrapperWorld::MainWorld()); + // For non main worlds, we can use any type to create a context. + TakeSnapshotForWorld( + creator, + *DOMWrapperWorld::Create( + isolate, DOMWrapperWorld::WorldType::kForV8ContextSnapshotNonMain)); + } + + isolate->RemoveMessageListeners(V8Initializer::MessageHandlerInMainThread); + + return creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear); +} + +v8::StartupData V8ContextSnapshot::SerializeInternalField( + v8::Local object, + int index, + void*) { + InternalFieldType field_type = InternalFieldType::kNone; + const WrapperTypeInfo* wrapper_type = ToWrapperTypeInfo(object); + if (kV8DOMWrapperObjectIndex == index) { + if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) { + field_type = InternalFieldType::kHTMLDocumentObject; + } + DCHECK_LE(kV8DefaultWrapperInternalFieldCount, + object->InternalFieldCount()); + } else if (kV8DOMWrapperTypeIndex == index) { + if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) { + field_type = InternalFieldType::kHTMLDocumentType; + } else if (blink::V8Document::wrapperTypeInfo.Equals(wrapper_type)) { + field_type = InternalFieldType::kDocumentType; + } else if (blink::V8Node::wrapperTypeInfo.Equals(wrapper_type)) { + field_type = InternalFieldType::kNodeType; + } + DCHECK_LE(kV8PrototypeInternalFieldcount, object->InternalFieldCount()); + } + CHECK_NE(field_type, InternalFieldType::kNone); + + int size = sizeof(InternalFieldType); + // Allocated memory on |data| will be released in + // v8::i::PartialSerializer::SerializeEmbedderFields(). + char* data = new char[size]; + std::memcpy(data, &field_type, size); + + return {data, size}; +} + +void V8ContextSnapshot::DeserializeInternalField(v8::Local object, + int index, + v8::StartupData payload, + void* ptr) { + // DeserializeInternalField() expects to be called in the main world + // with |document| being HTMLDocument. + CHECK_EQ(payload.raw_size, static_cast(sizeof(InternalFieldType))); + InternalFieldType type = + *reinterpret_cast(payload.data); + + const WrapperTypeInfo* wrapper_type_info = FieldTypeToWrapperTypeInfo(type); + switch (type) { + case InternalFieldType::kNodeType: + case InternalFieldType::kDocumentType: + case InternalFieldType::kHTMLDocumentType: { + CHECK_EQ(index, kV8DOMWrapperTypeIndex); + object->SetAlignedPointerInInternalField( + index, const_cast(wrapper_type_info)); + return; + } + case InternalFieldType::kHTMLDocumentObject: { + // The below code handles window.document on the main world. + CHECK_EQ(index, kV8DOMWrapperObjectIndex); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + DataForDeserializer* data = static_cast(ptr); + ScriptWrappable* document = data->document; + DCHECK(document); + + // Make reference from wrapper to document + object->SetAlignedPointerInInternalField(index, document); + // Make reference from document to wrapper + CHECK(document->SetWrapper(isolate, wrapper_type_info, object)); + WrapperTypeInfo::WrapperCreated(); + return; + } + case InternalFieldType::kNone: + NOTREACHED(); + return; + } + + NOTREACHED(); +} + +bool V8ContextSnapshot::CanCreateContextFromSnapshot( + v8::Isolate* isolate, + const DOMWrapperWorld& world, + Document* document) { + DCHECK(document); + if (V8PerIsolateData::From(isolate)->GetV8ContextSnapshotMode() != + V8PerIsolateData::V8ContextSnapshotMode::kUseSnapshot) { + return false; + } + + // When creating a context for the main world from snapshot, we also need a + // HTMLDocument instance. If typeof window.document is not HTMLDocument, e.g. + // SVGDocument or XMLDocument, we can't create contexts from the snapshot. + return !world.IsMainWorld() || document->IsHTMLDocument(); +} + +void V8ContextSnapshot::EnsureInterfaceTemplatesForWorld( + v8::Isolate* isolate, + const DOMWrapperWorld& world) { + V8PerIsolateData* data = V8PerIsolateData::From(isolate); + + // A snapshot has some interface templates in it. The first + // |kSnapshotInterfaceSize| templates are for the main world, and the + // remaining templates are for isolated worlds. + const int index_offset = world.IsMainWorld() ? 0 : kSnapshotInterfaceSize; + + for (size_t i = 0; i < kSnapshotInterfaceSize; ++i) { + auto& snapshot_interface = g_snapshot_interfaces[i]; + const WrapperTypeInfo* wrapper_type_info = + snapshot_interface.wrapper_type_info; + v8::Local interface_template = + v8::FunctionTemplate::FromSnapshot(isolate, index_offset + i) + .ToLocalChecked(); + snapshot_interface.install_function(isolate, world, interface_template); + CHECK(!interface_template.IsEmpty()); + data->SetInterfaceTemplate(world, wrapper_type_info, interface_template); + } +} + +void V8ContextSnapshot::TakeSnapshotForWorld(v8::SnapshotCreator* creator, + const DOMWrapperWorld& world) { + v8::Isolate* isolate = creator->GetIsolate(); + CHECK_EQ(isolate, v8::Isolate::GetCurrent()); + + // Function templates + v8::HandleScope handleScope(isolate); + std::array, kSnapshotInterfaceSize> + interface_templates; + v8::Local window_template; + for (size_t i = 0; i < kSnapshotInterfaceSize; ++i) { + const WrapperTypeInfo* wrapper_type_info = + g_snapshot_interfaces[i].wrapper_type_info; + v8::Local interface_template = + wrapper_type_info->domTemplate(isolate, world); + CHECK(!interface_template.IsEmpty()); + interface_templates[i] = interface_template; + if (V8Window::wrapperTypeInfo.Equals(wrapper_type_info)) { + window_template = interface_template; + } + } + CHECK(!window_template.IsEmpty()); + + v8::Local window_instance_template = + window_template->InstanceTemplate(); + CHECK(!window_instance_template.IsEmpty()); + + v8::Local context; + { + V8PerIsolateData::UseCounterDisabledScope use_counter_disabled( + V8PerIsolateData::From(isolate)); + context = v8::Context::New(isolate, nullptr, window_instance_template); + } + CHECK(!context.IsEmpty()); + + // For the main world context, we need to prepare a HTMLDocument wrapper and + // set it to window.documnt. + if (world.IsMainWorld()) { + v8::Context::Scope scope(context); + v8::Local document_wrapper = CreatePlainWrapper( + isolate, world, context, &V8HTMLDocument::wrapperTypeInfo); + int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex}; + void* values[] = {nullptr, const_cast( + &V8HTMLDocument::wrapperTypeInfo)}; + document_wrapper->SetAlignedPointerInInternalFields( + WTF_ARRAY_LENGTH(indices), indices, values); + + // Set the cached accessor for window.document. + CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(isolate).Set( + context->Global(), document_wrapper)); + } + + for (auto& interface_template : interface_templates) { + creator->AddTemplate(interface_template); + } + creator->AddContext(context, SerializeInternalField); + + V8PerIsolateData::From(isolate)->ClearPersistentsForV8ContextSnapshot(); +} + +} // namespace blink diff --git a/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.h b/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.h new file mode 100644 index 00000000000000..43233585064eb0 --- /dev/null +++ b/third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.h @@ -0,0 +1,79 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ContextSnapshot_h +#define V8ContextSnapshot_h + +#include "core/CoreExport.h" +#include "platform/wtf/Allocator.h" +#include "v8/include/v8.h" + +namespace blink { + +class Document; +class DOMWrapperWorld; + +// This class contains helper functions to take and use a V8 context snapshot. +// +// The V8 context snapshot is taken by tools/v8_context_snapshot/ when Chromium +// is built, and is used when Blink creates a new V8 context. When to build or +// to use the V8 context snapshot, you have a table of references of C++ +// callbacks exposed to V8. +// +// A V8 context snapshot contains: +// - Interface templates of Window, EventTarget, Node, Document, and +// HTMLDocument. +// - Two types of V8 contexts; one is for the main world, and the other is for +// other worlds. +// - HTMLDocument's wrapper (window.document) in the context for the main +// world. +// +// Currently, the V8 context snapshot supports only the main thread. If it is +// the main world, we need a special logic to serialize / deserialize +// window.document (so only HTMLDocument is supported on the main world). +// Worker threads are not yet supported. +class CORE_EXPORT V8ContextSnapshot { + STATIC_ONLY(V8ContextSnapshot); + + public: + static v8::Local CreateContextFromSnapshot( + v8::Isolate*, + const DOMWrapperWorld&, + v8::ExtensionConfiguration*, + v8::Local global_proxy, + Document*); + // Install runtime enabled features on some v8::Object's in a context + // created from a snapshot. + static void InstallRuntimeEnabledFeatures(v8::Local, Document*); + + static void EnsureInterfaceTemplates(v8::Isolate*); + + static void SetReferenceTable(intptr_t* table); + static intptr_t* GetReferenceTable(); + + // Do not call this in production. + static v8::StartupData TakeSnapshot(); + + private: + static v8::StartupData SerializeInternalField(v8::Local holder, + int index, + void* data); + static void DeserializeInternalField(v8::Local holder, + int index, + v8::StartupData payload, + void* data); + static bool CanCreateContextFromSnapshot(v8::Isolate*, + const DOMWrapperWorld&, + Document*); + + static void EnsureInterfaceTemplatesForWorld(v8::Isolate*, + const DOMWrapperWorld&); + + static void TakeSnapshotForWorld(v8::SnapshotCreator*, + const DOMWrapperWorld&); +}; + +} // namespace blink + +#endif // V8ContextSnapshot_h diff --git a/third_party/WebKit/Source/bindings/core/v8/V8DOMConfiguration.cpp b/third_party/WebKit/Source/bindings/core/v8/V8DOMConfiguration.cpp index 9bac22664ef6fd..5e78d3d913b798 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8DOMConfiguration.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/V8DOMConfiguration.cpp @@ -470,23 +470,26 @@ void InstallMethodInternal( v8::FunctionTemplate::New(isolate, callback, v8::Local(), signature, config.length); function_template->RemovePrototype(); - if (config.access_check_configuration == V8DOMConfiguration::kCheckAccess) + if (config.access_check_configuration == V8DOMConfiguration::kCheckAccess) { function_template->SetAcceptAnyReceiver(false); + } v8::Local function = function_template->GetFunction(isolate->GetCurrentContext()) .ToLocalChecked(); - if (location & V8DOMConfiguration::kOnInstance && !instance.IsEmpty()) + if (location & V8DOMConfiguration::kOnInstance && !instance.IsEmpty()) { instance ->DefineOwnProperty( isolate->GetCurrentContext(), name, function, static_cast(config.attribute)) .ToChecked(); - if (location & V8DOMConfiguration::kOnPrototype && !prototype.IsEmpty()) + } + if (location & V8DOMConfiguration::kOnPrototype && !prototype.IsEmpty()) { prototype ->DefineOwnProperty( isolate->GetCurrentContext(), name, function, static_cast(config.attribute)) .ToChecked(); + } } if (location & V8DOMConfiguration::kOnInterface && !interface.IsEmpty()) { // Operations installed on the interface object must be static @@ -753,16 +756,16 @@ v8::Local V8DOMConfiguration::DomClassTemplate( WrapperTypeInfo* wrapper_type_info, InstallTemplateFunction configure_dom_class_template) { V8PerIsolateData* data = V8PerIsolateData::From(isolate); - v8::Local result = + v8::Local interface_template = data->FindInterfaceTemplate(world, wrapper_type_info); - if (!result.IsEmpty()) - return result; + if (!interface_template.IsEmpty()) + return interface_template; - result = v8::FunctionTemplate::New( + interface_template = v8::FunctionTemplate::New( isolate, V8ObjectConstructor::IsValidConstructorMode); - configure_dom_class_template(isolate, world, result); - data->SetInterfaceTemplate(world, wrapper_type_info, result); - return result; + configure_dom_class_template(isolate, world, interface_template); + data->SetInterfaceTemplate(world, wrapper_type_info, interface_template); + return interface_template; } void V8DOMConfiguration::SetClassString( diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp index 29bbbaf414cfbe..0847a406c0bfdb 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp +++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.cpp @@ -35,6 +35,7 @@ #include "bindings/core/v8/SourceLocation.h" #include "bindings/core/v8/UseCounterCallback.h" #include "bindings/core/v8/V8BindingForCore.h" +#include "bindings/core/v8/V8ContextSnapshot.h" #include "bindings/core/v8/V8DOMException.h" #include "bindings/core/v8/V8ErrorEvent.h" #include "bindings/core/v8/V8ErrorHandler.h" @@ -388,6 +389,8 @@ static void InitializeV8Common(v8::Isolate* isolate) { isolate->SetUseCounterCallback(&UseCounterCallback); isolate->SetWasmModuleCallback(WasmModuleOverride); isolate->SetWasmInstanceCallback(WasmInstanceOverride); + + V8ContextSnapshot::EnsureInterfaceTemplates(isolate); } namespace { @@ -461,7 +464,7 @@ static void AdjustAmountOfExternalAllocatedMemory(int64_t diff) { v8::Isolate::GetCurrent()->AdjustAmountOfExternalAllocatedMemory(diff); } -void V8Initializer::InitializeMainThread() { +void V8Initializer::InitializeMainThread(intptr_t* reference_table) { DCHECK(IsMainThread()); WTF::ArrayBufferContents::Initialize(AdjustAmountOfExternalAllocatedMemory); @@ -475,13 +478,28 @@ void V8Initializer::InitializeMainThread() { // NOTE: Some threads (namely utility threads) don't have a scheduler. WebScheduler* scheduler = Platform::Current()->CurrentThread()->Scheduler(); + + V8PerIsolateData::V8ContextSnapshotMode v8_context_snapshot_mode = + Platform::Current()->IsTakingV8ContextSnapshot() + ? V8PerIsolateData::V8ContextSnapshotMode::kTakeSnapshot + : V8PerIsolateData::V8ContextSnapshotMode::kUseSnapshot; + if (v8_context_snapshot_mode == + V8PerIsolateData::V8ContextSnapshotMode::kUseSnapshot && + !RuntimeEnabledFeatures::V8ContextSnapshotEnabled()) { + v8_context_snapshot_mode = + V8PerIsolateData::V8ContextSnapshotMode::kDontUseSnapshot; + reference_table = nullptr; + } + V8ContextSnapshot::SetReferenceTable(reference_table); + // When timer task runner is used for PerIsolateData, GC tasks are getting // throttled and memory usage goes up. For now we're using loading task queue // to prevent this. // TODO(altimin): Consider switching to timerTaskRunner here. v8::Isolate* isolate = V8PerIsolateData::Initialize( scheduler ? scheduler->LoadingTaskRunner() - : Platform::Current()->CurrentThread()->GetWebTaskRunner()); + : Platform::Current()->CurrentThread()->GetWebTaskRunner(), + reference_table, v8_context_snapshot_mode); InitializeV8Common(isolate); diff --git a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.h b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.h index 991afda4dffbf2..6e27c2435d8900 100644 --- a/third_party/WebKit/Source/bindings/core/v8/V8Initializer.h +++ b/third_party/WebKit/Source/bindings/core/v8/V8Initializer.h @@ -36,7 +36,7 @@ class CORE_EXPORT V8Initializer { STATIC_ONLY(V8Initializer); public: - static void InitializeMainThread(); + static void InitializeMainThread(intptr_t* reference_table); static void InitializeWorker(v8::Isolate*); static void ReportRejectedPromisesOnMainThread(); diff --git a/third_party/WebKit/Source/bindings/modules/v8/BUILD.gn b/third_party/WebKit/Source/bindings/modules/v8/BUILD.gn index 28fbedfb346bb1..fb34ae8dfc4ea8 100644 --- a/third_party/WebKit/Source/bindings/modules/v8/BUILD.gn +++ b/third_party/WebKit/Source/bindings/modules/v8/BUILD.gn @@ -16,6 +16,7 @@ group("bindings_modules_v8_generated") { ":generate_bindings_modules_v8_interfaces", ":generate_bindings_modules_v8_partial_interfaces", ":generate_bindings_modules_v8_partial_interfaces_for_testing", + ":generate_v8_context_snapshot_external_references", ] } @@ -108,3 +109,37 @@ blink_modules_sources("bindings_modules_impl") { split_count = 5 } } + +action("generate_v8_context_snapshot_external_references") { + script = "$bindings_scripts_dir/generate_v8_context_snapshot_external_references.py" + idl_files = core_idl_files + core_idl_with_modules_dependency_files + + modules_definition_idl_files + output = bindings_generated_v8_context_snapshot_external_references_file + + inputs = idl_files + [ script ] + outputs = [ + output, + ] + + response_file_contents = rebase_path(idl_files, root_build_dir) + args = [ + "--cache-dir", + rebase_path(bindings_scripts_output_dir, root_build_dir), + "--output", + rebase_path(output, root_build_dir), + "--idl-files-list", + "{{response_file_name}}", + "--info-dir", + rebase_path("$bindings_output_dir", root_build_dir), + "--target-component", + "modules", + ] + + deps = [ + "//third_party/WebKit/Source/bindings/core:interfaces_info_individual_core", + "//third_party/WebKit/Source/bindings/modules:interfaces_info", + "//third_party/WebKit/Source/bindings/modules:interfaces_info_individual_modules", + "//third_party/WebKit/Source/bindings/scripts:cached_jinja_templates", + "//third_party/WebKit/Source/bindings/scripts:cached_lex_yacc_tables", + ] +} diff --git a/third_party/WebKit/Source/bindings/modules/v8/V8ContextSnapshotExternalReferences.h b/third_party/WebKit/Source/bindings/modules/v8/V8ContextSnapshotExternalReferences.h new file mode 100644 index 00000000000000..3c7645eb4e1b8f --- /dev/null +++ b/third_party/WebKit/Source/bindings/modules/v8/V8ContextSnapshotExternalReferences.h @@ -0,0 +1,29 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8ContextSnapshotExternalReferences_h +#define V8ContextSnapshotExternalReferences_h + +#include + +#include "modules/ModulesExport.h" +#include "platform/wtf/Allocator.h" + +namespace blink { + +// V8ContextSnapshotExternalReferences::GetTable() provides a table of pointers +// of C++ callbacks exposed to V8. The table contains C++ callbacks for DOM +// attribute getters, setters, DOM methods, wrapper type info etc. +class MODULES_EXPORT V8ContextSnapshotExternalReferences { + STATIC_ONLY(V8ContextSnapshotExternalReferences); + + public: + // The definition of this method is auto-generated in + // V8ContextSnapshotExternalReferences.cpp. + static intptr_t* GetTable(); +}; + +} // namespace blink + +#endif // V8ContextSnapshotExternalReferences_h diff --git a/third_party/WebKit/Source/bindings/modules/v8/generated.gni b/third_party/WebKit/Source/bindings/modules/v8/generated.gni index 23d68dc96e2553..530cecb61a2917 100644 --- a/third_party/WebKit/Source/bindings/modules/v8/generated.gni +++ b/third_party/WebKit/Source/bindings/modules/v8/generated.gni @@ -86,3 +86,6 @@ generated_modules_callback_function_files = [ "$bindings_modules_v8_output_dir/RemotePlaybackAvailabilityCallback.cpp", "$bindings_modules_v8_output_dir/RemotePlaybackAvailabilityCallback.h", ] + +bindings_generated_v8_context_snapshot_external_references_file = + "$bindings_modules_v8_output_dir/V8ContextSnapshotExternalReferences.cpp" diff --git a/third_party/WebKit/Source/bindings/modules/v8/v8.gni b/third_party/WebKit/Source/bindings/modules/v8/v8.gni index 286f5b71c906a9..6cba506e968803 100644 --- a/third_party/WebKit/Source/bindings/modules/v8/v8.gni +++ b/third_party/WebKit/Source/bindings/modules/v8/v8.gni @@ -19,6 +19,7 @@ bindings_modules_v8_files = "ToV8ForModules.h", "V8BindingForModules.cpp", "V8BindingForModules.h", + "V8ContextSnapshotExternalReferences.h", "wasm/WasmResponseExtensions.cpp", "wasm/WasmResponseExtensions.h", "WebGLAny.cpp", diff --git a/third_party/WebKit/Source/bindings/scripts/generate_v8_context_snapshot_external_references.py b/third_party/WebKit/Source/bindings/scripts/generate_v8_context_snapshot_external_references.py new file mode 100644 index 00000000000000..3795d1ee2e2090 --- /dev/null +++ b/third_party/WebKit/Source/bindings/scripts/generate_v8_context_snapshot_external_references.py @@ -0,0 +1,223 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# pylint: disable=relative-import + +import argparse +import os + +from code_generator import initialize_jinja_env +from idl_reader import IdlReader +from utilities import create_component_info_provider, write_file +import utilities +import v8_attributes +import v8_interface +import v8_types +import v8_utilities + + +INCLUDES = frozenset([ + 'bindings/core/v8/GeneratedCodeHelper.h', + 'bindings/core/v8/V8HTMLDocument.h', + 'bindings/core/v8/V8Initializer.h', + 'bindings/core/v8/V8Window.h', + 'platform/bindings/DOMWrapperWorld.h', + 'platform/bindings/V8ObjectConstructor.h', + 'platform/bindings/V8PerIsolateData.h', + 'platform/bindings/V8PrivateProperty.h', + 'v8/include/v8.h']) + +TEMPLATE_FILE = 'external_reference_table.cpp.tmpl' + +WHITE_LIST_INTERFACES = frozenset([ + 'DOMMatrix', # crbug.com/733481 +]) + +SNAPSHOTTED_INTERFACES = frozenset([ + 'Window', + 'EventTarget', + 'HTMLDocument', + 'Document', + 'Node', +]) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--idl-files-list', type=str, required=True, + help='file listing IDL files') + parser.add_argument('--output', type=str, required=True, + help='output file path') + parser.add_argument('--info-dir', type=str, required=True, + help='directory contains component info') + parser.add_argument('--cache-dir', type=str, required=True, + help='cache directory') + parser.add_argument('--target-component', type=str, required=True, + help='target component') + return parser.parse_known_args() + + +# This class creates a Jinja template context about an interface. +class InterfaceTemplateContextBuilder(object): + + def __init__(self, opts, info_provider): + self._opts = opts + self._info_provider = info_provider + + def create_interface_context(self, interface, interfaces): + '''Creates a Jinja context which is based on an interface.''' + + name = '%s%s' % (v8_utilities.cpp_name(interface), 'Partial' if interface.is_partial else '') + + # Constructors + constructors = any(constructor.name == 'Constructor' for constructor in interface.constructors) + custom_constructors = interface.custom_constructors + html_constructor = 'HTMLConstructor' in interface.extended_attributes + has_constructor_callback = constructors or custom_constructors or html_constructor + + attributes = [] + methods = [] + has_cross_origin_indexed_getter = False + has_cross_origin_named_enum = False + has_cross_origin_named_getter = False + has_cross_origin_named_setter = False + has_origin_safe_method_setter = False + has_security_check = False + indexed_property_getter = None + is_global = False + named_property_getter = None + if interface.name in SNAPSHOTTED_INTERFACES: + attributes = [v8_attributes.attribute_context(interface, attribute, interfaces) + for attribute in interface.attributes] + methods = v8_interface.methods_context(interface)['methods'] + is_global = ('PrimaryGlobal' in interface.extended_attributes or + 'Global' in interface.extended_attributes) + + named_property_getter = v8_interface.property_getter( + interface.named_property_getter, ['name']) + indexed_property_getter = v8_interface.property_getter( + interface.indexed_property_getter, ['index']) + + if not interface.is_partial: + has_origin_safe_method_setter = is_global and any( + method['is_check_security_for_receiver'] and not method['is_unforgeable'] + for method in methods) + has_security_check = ('CheckSecurity' in interface.extended_attributes and + interface.name != 'EventTarget') + has_cross_origin_named_getter = (any(method['is_cross_origin'] for method in methods) or + any(attribute['has_cross_origin_getter'] for attribute in attributes)) + has_cross_origin_named_setter = any(attribute['has_cross_origin_setter'] for attribute in attributes) + has_cross_origin_indexed_getter = indexed_property_getter and indexed_property_getter['is_cross_origin'] + has_cross_origin_named_enum = has_cross_origin_named_getter or has_cross_origin_named_setter + if named_property_getter and named_property_getter['is_cross_origin']: + has_cross_origin_named_getter = True + + return { + 'attributes': attributes, + 'has_origin_safe_method_setter': has_origin_safe_method_setter, + 'has_constructor_callback': has_constructor_callback, + 'has_cross_origin_named_getter': has_cross_origin_named_getter, + 'has_cross_origin_named_setter': has_cross_origin_named_setter, + 'has_cross_origin_named_enumerator': has_cross_origin_named_enum, + 'has_cross_origin_indexed_getter': has_cross_origin_indexed_getter, + 'has_security_check': has_security_check, + 'indexed_property_getter': indexed_property_getter, + 'indexed_property_setter': v8_interface.property_setter(interface.indexed_property_setter, interface), + 'indexed_property_deleter': v8_interface.property_deleter(interface.indexed_property_deleter), + 'is_array_buffer_or_view': interface.idl_type.is_array_buffer_or_view, + 'is_callback': interface.is_callback, + 'is_partial': interface.is_partial, + 'is_snapshotted': interface in SNAPSHOTTED_INTERFACES, + 'methods': methods, + 'name': name, + 'named_constructor': v8_interface.named_constructor_context(interface), + 'named_property_getter': named_property_getter, + 'named_property_setter': v8_interface.property_setter(interface.named_property_setter, interface), + 'named_property_deleter': v8_interface.property_deleter(interface.named_property_deleter), + 'v8_name': v8_utilities.v8_class_name_or_partial(interface), + } + + +# This class applies a Jinja template and creates a .cpp file for the external reference table. +class ExternalReferenceTableGenerator(object): + def __init__(self, opts, info_provider): + self._opts = opts + self._info_provider = info_provider + self._reader = IdlReader( + info_provider.interfaces_info, opts.cache_dir) + self._interface_contexts = {} + self._include_files = set(INCLUDES) + v8_types.set_component_dirs(info_provider.interfaces_info['component_dirs']) + + # Creates a Jinja context from an IDL file. + def process_idl_file(self, idl_filename): + definitions = self._reader.read_idl_definitions(idl_filename) + base_name, _ = os.path.splitext(os.path.basename(idl_filename)) + for component in definitions: + target_definitions = definitions[component] + interfaces = target_definitions.interfaces + if base_name in interfaces.keys(): + interface = interfaces[base_name] + self._process_interface(interface, component, interfaces) + + # Creates a Jinja context from an interface. Some interfaces are not used + # in V8 context snapshot, so we can skip them. + def _process_interface(self, interface, component, interfaces): + def has_impl(interface): + if interface.name in WHITE_LIST_INTERFACES: + return True + # Non legacy callback interface does not provide V8 callbacks. + if interface.is_callback: + return len(interface.constants) > 0 + if 'RuntimeEnabled' in interface.extended_attributes: + return False + return True + + if not has_impl(interface): + return + + context_builder = InterfaceTemplateContextBuilder(self._opts, self._info_provider) + context = context_builder.create_interface_context(interface, interfaces) + name = '%s%s' % (interface.name, 'Partial' if interface.is_partial else '') + self._interface_contexts[name] = context + include_file = 'bindings/%s/v8/%s.h' % (component, context['v8_name']) + self._include_files.add(include_file) + + # Gathers all interface-dependent information and returns as a Jinja template context. + def _create_template_context(self): + interfaces = [] + for name in sorted(self._interface_contexts): + interfaces.append(self._interface_contexts[name]) + return { + 'class': 'V8ContextSnapshotExternalReferences', + 'interfaces': interfaces, + 'include_files': sorted(list(self._include_files)), + } + + # Applies a Jinja template on a context and generates a C++ code. + def generate(self): + jinja_env = initialize_jinja_env(self._opts.cache_dir) + context = self._create_template_context() + cpp_template = jinja_env.get_template(TEMPLATE_FILE) + cpp_text = cpp_template.render(context) + return cpp_text + + +def main(): + opts, _ = parse_args() + # TODO(peria): get rid of |info_provider| + info_provider = create_component_info_provider( + opts.info_dir, opts.target_component) + generator = ExternalReferenceTableGenerator(opts, info_provider) + + idl_files = utilities.read_idl_files_list_from_file(opts.idl_files_list, False) + for idl_file in idl_files: + generator.process_idl_file(idl_file) + output_code = generator.generate() + output_path = opts.output + write_file(output_code, output_path) + + +if __name__ == '__main__': + main() diff --git a/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl new file mode 100644 index 00000000000000..96d05bb32d885b --- /dev/null +++ b/third_party/WebKit/Source/bindings/templates/external_reference_table.cpp.tmpl @@ -0,0 +1,125 @@ +{% filter format_blink_cpp_source_code %} + +{% include 'copyright_block.txt' %} + +#include "bindings/modules/v8/{{class}}.h" + +#include + +{% for include_file in include_files %} +#include "{{include_file}}" +{% endfor %} + +namespace blink { + +intptr_t* {{class}}::GetTable() { + static intptr_t reference_table[] = { + {% for interface in interfaces %} + {% if not interface.is_array_buffer_or_view %} + {% set v8_class = interface.v8_name %} + + {# Attributes #} + {%- for attribute in interface.attributes %} + {%- for world_suffix in attribute.world_suffixes %} + {% if not attribute.constructor_type %} + reinterpret_cast({{v8_class}}::{{attribute.name}}AttributeGetterCallback{{world_suffix}}), + {% elif attribute.needs_constructor_getter_callback %} + reinterpret_cast({{v8_class}}::{{attribute.name}}ConstructorGetterCallback{{world_suffix}}), + {% elif attribute.is_named_constructor %} + reinterpret_cast(V8{{attribute.constructor_type}}::NamedConstructorAttributeGetter{{world_suffix}}), + {% endif %} + {% if attribute.has_setter %} + reinterpret_cast({{v8_class}}::{{attribute.name}}AttributeSetterCallback{{world_suffix}}), + {% endif %} + {%- endfor %}{# world_suffix #} + {%- endfor %}{# attributes #} + + {# Methods / Operations #} + {% for method in interface.methods %} + {%- for world_suffix in method.world_suffixes %} + {% if not method.overload_index or method.overloads %} + {% if (method.overloads and method.overloads.visible and + (not method.overloads.has_partial_overloads or not interface.is_partial)) or + (not method.overloads and method.visible) %} + {# TODO(bashi): Remove this 'if' condition when crbug.com/630986 is fixed. #} + {% if not interface.is_callback %} + reinterpret_cast({{v8_class}}::{{method.name}}MethodCallback{{world_suffix}}), + {% endif %} + {% endif %} + {% endif %}{# overload(_index) #} + {% if method.is_cross_origin and method.visible %} + reinterpret_cast({{v8_class}}::{{method.name}}OriginSafeMethodGetterCallback{{world_suffix}}), + {% endif%} + {% endfor %} + {% endfor %}{# method #} + {% if interface.has_origin_safe_method_setter %} + reinterpret_cast({{v8_class}}::{{interface.name}}OriginSafeMethodSetterCallback), + {% endif %} + {% if interface.has_cross_origin_named_getter %} + reinterpret_cast({{v8_class}}::crossOriginNamedGetter), + {% endif %} + {% if interface.has_cross_origin_named_setter %} + reinterpret_cast({{v8_class}}::crossOriginNamedSetter), + {% endif %} + {% if interface.has_cross_origin_named_enumerator %} + reinterpret_cast({{v8_class}}::crossOriginNamedEnumerator), + {% endif %} + {% if interface.has_cross_origin_indexed_getter %} + reinterpret_cast({{v8_class}}::crossOriginIndexedGetter), + {% endif %} + {% if interface.has_security_check %} + reinterpret_cast({{v8_class}}::securityCheck), + {% endif %} + + {# Other properties #} + {% if interface.has_constructor_callback %} + reinterpret_cast({{v8_class}}::constructorCallback), + {% endif %} + {% if interface.named_property_getter %} + reinterpret_cast({{v8_class}}::namedPropertyGetterCallback), + {% endif %} + {%- if interface.named_property_setter %} + reinterpret_cast({{v8_class}}::namedPropertySetterCallback), + {% endif %} + {%- if interface.named_property_deleter %} + reinterpret_cast({{v8_class}}::namedPropertyDeleterCallback), + {% endif %} + {%- if interface.named_property_getter and interface.named_property_getter.is_enumerable %} + reinterpret_cast({{v8_class}}::namedPropertyQueryCallback), + reinterpret_cast({{v8_class}}::namedPropertyEnumeratorCallback), + {% endif %} + {%- if interface.indexed_property_getter %} + reinterpret_cast({{v8_class}}::indexedPropertyGetterCallback), + reinterpret_cast({{v8_class}}::indexedPropertyDefinerCallback), + reinterpret_cast({{v8_class}}::indexedPropertyDescriptorCallback), + {% endif %} + {%- if interface.indexed_property_getter or interface.named_property_setter %} + reinterpret_cast({{v8_class}}::indexedPropertySetterCallback), + {% endif %} + {%- if interface.indexed_property_deleter %} + reinterpret_cast({{v8_class}}::indexedPropertyDeleterCallback), + {% endif %} + {% if interface.has_security_check_function %} + reinterpret_cast({{v8_class}}::securityCheck), + {%- endif %} + {% endif %}{# not is_array_buffer_or_view #} + + {% if not interface.is_partial or is_snapshotted %} + reinterpret_cast(&{{interface.v8_name}}::wrapperTypeInfo), + {% endif %} + {% if interface.named_constructor %} + reinterpret_cast(&{{interface.v8_name}}Constructor::wrapperTypeInfo), + {% endif %} + {% endfor %}{# interfaces #} + + reinterpret_cast(V8ObjectConstructor::IsValidConstructorMode), + reinterpret_cast(V8ConstructorAttributeGetter), + 0 // terminate with a null + }; + + return reference_table; +} + +} // namespace blink + +{% endfilter %}{# format_blink_cpp_source_code #} diff --git a/third_party/WebKit/Source/bindings/templates/templates.gni b/third_party/WebKit/Source/bindings/templates/templates.gni index d505e2e510ff5f..ded9ffd14b979f 100644 --- a/third_party/WebKit/Source/bindings/templates/templates.gni +++ b/third_party/WebKit/Source/bindings/templates/templates.gni @@ -11,15 +11,15 @@ code_generator_template_files = "callback_interface.cpp.tmpl", "callback_interface.h.tmpl", "constants.cpp.tmpl", - "utilities.cpp.tmpl", "copyright_block.txt", "dictionary_impl.cpp.tmpl", "dictionary_impl.h.tmpl", "dictionary_v8.cpp.tmpl", "dictionary_v8.h.tmpl", - "interface_base.cpp.tmpl", + "external_reference_table.cpp.tmpl", "interface.cpp.tmpl", "interface.h.tmpl", + "interface_base.cpp.tmpl", "legacy_callback_interface.cpp.tmpl", "legacy_callback_interface.h.tmpl", "methods.cpp.tmpl", @@ -27,6 +27,7 @@ code_generator_template_files = "partial_interface.h.tmpl", "union_container.cpp.tmpl", "union_container.h.tmpl", + "utilities.cpp.tmpl", "web_agent_api_interface.cc.tmpl", "web_agent_api_interface.h.tmpl", ], diff --git a/third_party/WebKit/Source/controller/BlinkInitializer.cpp b/third_party/WebKit/Source/controller/BlinkInitializer.cpp index a314d18a3e906b..75f35353677d36 100644 --- a/third_party/WebKit/Source/controller/BlinkInitializer.cpp +++ b/third_party/WebKit/Source/controller/BlinkInitializer.cpp @@ -29,6 +29,7 @@ */ #include "bindings/core/v8/V8Initializer.h" +#include "bindings/modules/v8/V8ContextSnapshotExternalReferences.h" #include "core/animation/AnimationClock.h" #include "modules/ModulesInitializer.h" #include "platform/bindings/Microtask.h" @@ -69,8 +70,8 @@ static ModulesInitializer& GetModulesInitializer() { void Initialize(Platform* platform) { Platform::Initialize(platform); - V8Initializer::InitializeMainThread(); - + V8Initializer::InitializeMainThread( + V8ContextSnapshotExternalReferences::GetTable()); GetModulesInitializer().Initialize(); WebFactoryImpl::Initialize(); diff --git a/third_party/WebKit/Source/core/exported/BUILD.gn b/third_party/WebKit/Source/core/exported/BUILD.gn index 51775a5c03ea17..57a9fd68979644 100644 --- a/third_party/WebKit/Source/core/exported/BUILD.gn +++ b/third_party/WebKit/Source/core/exported/BUILD.gn @@ -91,6 +91,7 @@ blink_core_sources("exported") { "WebTextCheckingResult.cpp", "WebUserGestureIndicator.cpp", "WebUserGestureToken.cpp", + "WebV8ContextSnapshot.cpp", "WebViewBase.cpp", "WebViewBase.h", "WebViewImpl.cpp", diff --git a/third_party/WebKit/Source/core/exported/WebV8ContextSnapshot.cpp b/third_party/WebKit/Source/core/exported/WebV8ContextSnapshot.cpp new file mode 100644 index 00000000000000..340f78cc5fda08 --- /dev/null +++ b/third_party/WebKit/Source/core/exported/WebV8ContextSnapshot.cpp @@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium 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 "public/web/WebV8ContextSnapshot.h" + +#include "bindings/core/v8/V8ContextSnapshot.h" +#include "v8/include/v8.h" + +namespace blink { + +v8::StartupData WebV8ContextSnapshot::TakeSnapshot() { + return V8ContextSnapshot::TakeSnapshot(); +} + +} // namespace blink diff --git a/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp b/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp index a632bb795150d1..aa749891b75379 100644 --- a/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp +++ b/third_party/WebKit/Source/core/workers/WorkerBackingThread.cpp @@ -6,6 +6,7 @@ #include #include "bindings/core/v8/V8BindingForCore.h" +#include "bindings/core/v8/V8ContextSnapshot.h" #include "bindings/core/v8/V8GCController.h" #include "bindings/core/v8/V8IdleTaskRunner.h" #include "bindings/core/v8/V8Initializer.h" @@ -64,8 +65,12 @@ void WorkerBackingThread::InitializeOnBackingThread( backing_thread_->InitializeOnThread(); DCHECK(!isolate_); + // TODO(peria): Replace GetReferenceTable with nullptr. + // (http://crbug.com/v8/6448) isolate_ = V8PerIsolateData::Initialize( - backing_thread_->PlatformThread().GetWebTaskRunner()); + backing_thread_->PlatformThread().GetWebTaskRunner(), + V8ContextSnapshot::GetReferenceTable(), + V8PerIsolateData::V8ContextSnapshotMode::kDontUseSnapshot); AddWorkerIsolate(isolate_); V8Initializer::InitializeWorker(isolate_); diff --git a/third_party/WebKit/Source/modules/BUILD.gn b/third_party/WebKit/Source/modules/BUILD.gn index 82ba538e2f3515..e7b85168d3abf4 100644 --- a/third_party/WebKit/Source/modules/BUILD.gn +++ b/third_party/WebKit/Source/modules/BUILD.gn @@ -66,6 +66,10 @@ target("jumbo_" + modules_target_type, "modules") { sources += get_target_outputs(":module_names") sources += bindings_modules_v8_files + sources += rebase_path( + [ bindings_generated_v8_context_snapshot_external_references_file ], + ".", + "//") configs += [ ":modules_implementation", diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 index b7a15787489783..86a481a0038442 100644 --- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 +++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 @@ -1067,6 +1067,10 @@ name: "UpdateHoverPostLayout", status: "experimental", }, + { + name: "V8ContextSnapshot", + status: "test", + }, { name: "V8IdleTasks", }, diff --git a/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.cpp b/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.cpp index ef4500a6e5070d..98f4f4bfc26b29 100644 --- a/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.cpp +++ b/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.cpp @@ -87,6 +87,7 @@ DOMWrapperWorld::DOMWrapperWorld(v8::Isolate* isolate, case WorldType::kGarbageCollector: case WorldType::kRegExp: case WorldType::kTesting: + case WorldType::kForV8ContextSnapshotNonMain: case WorldType::kWorker: { WorldMap& map = GetWorldMap(); DCHECK(!map.Contains(world_id_)); @@ -288,6 +289,7 @@ int DOMWrapperWorld::GenerateWorldIdForType(WorldType world_type) { case WorldType::kGarbageCollector: case WorldType::kRegExp: case WorldType::kTesting: + case WorldType::kForV8ContextSnapshotNonMain: case WorldType::kWorker: int world_id = *next_world_id; CHECK_GE(world_id, WorldId::kUnspecifiedWorldIdStart); diff --git a/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.h b/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.h index 77646d13c2909d..c620e75716c26e 100644 --- a/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.h +++ b/third_party/WebKit/Source/platform/bindings/DOMWrapperWorld.h @@ -72,6 +72,7 @@ class PLATFORM_EXPORT DOMWrapperWorld : public RefCounted { kGarbageCollector, kRegExp, kTesting, + kForV8ContextSnapshotNonMain, kWorker, }; diff --git a/third_party/WebKit/Source/platform/bindings/ScriptWrappable.h b/third_party/WebKit/Source/platform/bindings/ScriptWrappable.h index cc1358d0d0dc01..b55d242a7d7a89 100644 --- a/third_party/WebKit/Source/platform/bindings/ScriptWrappable.h +++ b/third_party/WebKit/Source/platform/bindings/ScriptWrappable.h @@ -155,7 +155,7 @@ class PLATFORM_EXPORT ScriptWrappable : public TraceWrapperBase { void MarkWrapper(const WrapperVisitor*) const; private: - // These classes are exceptionally allowed to use mainWorldWrapper(). + // These classes are exceptionally allowed to use MainWorldWrapper(). friend class DOMDataStore; friend class HeapSnaphotWrapperVisitor; friend class V8HiddenValue; diff --git a/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp b/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp index 5aadd3903d6e43..0beda4b7ab18a2 100644 --- a/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp +++ b/third_party/WebKit/Source/platform/bindings/V8PerContextData.cpp @@ -136,9 +136,7 @@ v8::Local V8PerContextData::ConstructorForTypeSlowCase( // Origin Trials InstallConditionalFeatures(type, ScriptState::From(current_context), prototype_object, interface_object); - constructor_map_.Set(type, interface_object); - return interface_object; } diff --git a/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.cpp b/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.cpp index 6ba47d5d3947f9..35dafe98777b62 100644 --- a/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.cpp +++ b/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.cpp @@ -27,6 +27,7 @@ #include +#include "platform/RuntimeEnabledFeatures.h" #include "platform/ScriptForbiddenScope.h" #include "platform/WebTaskRunner.h" #include "platform/bindings/DOMDataStore.h" @@ -52,18 +53,34 @@ static void MicrotasksCompletedCallback(v8::Isolate* isolate) { V8PerIsolateData::From(isolate)->RunEndOfScopeTasks(); } -V8PerIsolateData::V8PerIsolateData(WebTaskRunner* task_runner) - : isolate_holder_( +V8PerIsolateData::V8PerIsolateData( + WebTaskRunner* task_runner, + intptr_t* table, + V8ContextSnapshotMode v8_context_snapshot_mode) + : v8_context_snapshot_mode_(v8_context_snapshot_mode), + isolate_holder_( task_runner ? task_runner->ToSingleThreadTaskRunner() : nullptr, gin::IsolateHolder::kSingleThread, IsMainThread() ? gin::IsolateHolder::kDisallowAtomicsWait - : gin::IsolateHolder::kAllowAtomicsWait), + : gin::IsolateHolder::kAllowAtomicsWait, + table, + v8_context_snapshot_mode_ == V8ContextSnapshotMode::kUseSnapshot + ? &startup_data_ + : nullptr), + interface_template_map_for_v8_context_snapshot_(GetIsolate()), string_cache_(WTF::WrapUnique(new StringCache(GetIsolate()))), private_property_(V8PrivateProperty::Create()), constructor_mode_(ConstructorMode::kCreateNewObject), use_counter_disabled_(false), is_handling_recursion_level_error_(false), is_reporting_exception_(false) { + // If it fails to load the snapshot file, falls back to kDontUseSnapshot mode. + // TODO(peria): Remove this fallback routine. + if (v8_context_snapshot_mode_ == V8ContextSnapshotMode::kUseSnapshot && + !startup_data_.data) { + v8_context_snapshot_mode_ = V8ContextSnapshotMode::kDontUseSnapshot; + } + // FIXME: Remove once all v8::Isolate::GetCurrent() calls are gone. GetIsolate()->Enter(); GetIsolate()->AddBeforeCallEnteredCallback(&BeforeCallEnteredCallback); @@ -72,6 +89,24 @@ V8PerIsolateData::V8PerIsolateData(WebTaskRunner* task_runner) g_main_thread_per_isolate_data = this; } +// This constructor is used for taking a V8 context snapshot. It must run on the +// main thread. +V8PerIsolateData::V8PerIsolateData(intptr_t* reference_table) + : v8_context_snapshot_mode_(V8ContextSnapshotMode::kTakeSnapshot), + isolate_holder_(reference_table, nullptr), + interface_template_map_for_v8_context_snapshot_(GetIsolate()), + string_cache_(WTF::WrapUnique(new StringCache(GetIsolate()))), + private_property_(V8PrivateProperty::Create()), + constructor_mode_(ConstructorMode::kCreateNewObject), + use_counter_disabled_(false), + is_handling_recursion_level_error_(false), + is_reporting_exception_(false) { + CHECK(IsMainThread()); + + // SnapshotCreator enters the isolate, so we don't call Isolate::Enter() here. + g_main_thread_per_isolate_data = this; +} + V8PerIsolateData::~V8PerIsolateData() {} v8::Isolate* V8PerIsolateData::MainThreadIsolate() { @@ -79,8 +114,21 @@ v8::Isolate* V8PerIsolateData::MainThreadIsolate() { return g_main_thread_per_isolate_data->GetIsolate(); } -v8::Isolate* V8PerIsolateData::Initialize(WebTaskRunner* task_runner) { - V8PerIsolateData* data = new V8PerIsolateData(task_runner); +v8::Isolate* V8PerIsolateData::Initialize(WebTaskRunner* task_runner, + intptr_t* reference_table, + V8ContextSnapshotMode context_mode) { + DCHECK(context_mode == V8ContextSnapshotMode::kDontUseSnapshot || + reference_table); + + V8PerIsolateData* data = nullptr; + if (context_mode == V8ContextSnapshotMode::kTakeSnapshot) { + CHECK(reference_table); + data = new V8PerIsolateData(reference_table); + } else { + data = new V8PerIsolateData(task_runner, reference_table, context_mode); + } + DCHECK(data); + v8::Isolate* isolate = data->GetIsolate(); isolate->SetData(gin::kEmbedderBlink, data); return isolate; @@ -164,6 +212,11 @@ v8::Local V8PerIsolateData::FindOrCreateOperationTemplate( v8::Local V8PerIsolateData::FindInterfaceTemplate( const DOMWrapperWorld& world, const void* key) { + if (GetV8ContextSnapshotMode() == V8ContextSnapshotMode::kTakeSnapshot) { + const WrapperTypeInfo* type = reinterpret_cast(key); + return interface_template_map_for_v8_context_snapshot_.Get(type); + } + auto& map = SelectInterfaceTemplateMap(world); auto result = map.find(key); if (result != map.end()) @@ -175,8 +228,19 @@ void V8PerIsolateData::SetInterfaceTemplate( const DOMWrapperWorld& world, const void* key, v8::Local value) { - auto& map = SelectInterfaceTemplateMap(world); - map.insert(key, v8::Eternal(GetIsolate(), value)); + if (GetV8ContextSnapshotMode() == V8ContextSnapshotMode::kTakeSnapshot) { + auto& map = interface_template_map_for_v8_context_snapshot_; + const WrapperTypeInfo* type = reinterpret_cast(key); + map.Set(type, value); + } else { + auto& map = SelectInterfaceTemplateMap(world); + map.insert(key, v8::Eternal(GetIsolate(), value)); + } +} + +void V8PerIsolateData::ClearPersistentsForV8ContextSnapshot() { + interface_template_map_for_v8_context_snapshot_.Clear(); + private_property_.reset(); } const v8::Eternal* V8PerIsolateData::FindOrCreateEternalNameCache( diff --git a/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.h b/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.h index 8af0f25c01ae50..2e52788a19eca7 100644 --- a/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.h +++ b/third_party/WebKit/Source/platform/bindings/V8PerIsolateData.h @@ -60,6 +60,12 @@ class PLATFORM_EXPORT V8PerIsolateData { WTF_MAKE_NONCOPYABLE(V8PerIsolateData); public: + enum class V8ContextSnapshotMode { + kTakeSnapshot, + kDontUseSnapshot, + kUseSnapshot, + }; + class EndOfScopeTask { USING_FAST_MALLOC(EndOfScopeTask); @@ -101,7 +107,9 @@ class PLATFORM_EXPORT V8PerIsolateData { virtual ~Data() = default; }; - static v8::Isolate* Initialize(WebTaskRunner*); + static v8::Isolate* Initialize(WebTaskRunner*, + intptr_t* refernce_table, + V8ContextSnapshotMode); static V8PerIsolateData* From(v8::Isolate* isolate) { DCHECK(isolate); @@ -144,6 +152,17 @@ class PLATFORM_EXPORT V8PerIsolateData { const void* key, v8::Local); + // When v8::SnapshotCreator::CreateBlob() is called, we must not have + // persistent handles in Blink. This method clears them. + void ClearPersistentsForV8ContextSnapshot(); + + v8::SnapshotCreator* GetSnapshotCreator() const { + return isolate_holder_.snapshot_creator(); + } + V8ContextSnapshotMode GetV8ContextSnapshotMode() const { + return v8_context_snapshot_mode_; + } + // Accessor to the cache of cross-origin accessible operation's templates. // Created templates get automatically cached. v8::Local FindOrCreateOperationTemplate( @@ -224,11 +243,14 @@ class PLATFORM_EXPORT V8PerIsolateData { } private: - explicit V8PerIsolateData(WebTaskRunner*); + V8PerIsolateData(WebTaskRunner*, + intptr_t* reference_table, + V8ContextSnapshotMode); + explicit V8PerIsolateData(intptr_t* reference_table); ~V8PerIsolateData(); - typedef HashMap> - V8FunctionTemplateMap; + using V8FunctionTemplateMap = + HashMap>; V8FunctionTemplateMap& SelectInterfaceTemplateMap(const DOMWrapperWorld&); V8FunctionTemplateMap& SelectOperationTemplateMap(const DOMWrapperWorld&); bool HasInstance(const WrapperTypeInfo* untrusted, @@ -238,12 +260,16 @@ class PLATFORM_EXPORT V8PerIsolateData { v8::Local, V8FunctionTemplateMap&); + V8ContextSnapshotMode v8_context_snapshot_mode_; + // This isolate_holder_ must be initialized before initializing some other + // members below. gin::IsolateHolder isolate_holder_; - // m_interfaceTemplateMapFor{,Non}MainWorld holds function templates for + // 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_; @@ -252,6 +278,16 @@ class PLATFORM_EXPORT V8PerIsolateData { // Contains lists of eternal names, such as dictionary keys. HashMap>> eternal_name_cache_; + // Members required for the V8 context snapshot. + // v8::Context is created from this blob data image. This needs to be + // instantiated before |isolate_holder_| gets instantiated. + v8::StartupData startup_data_; + // When taking a V8 context snapshot, we can't keep V8 objects with eternal + // handles. So we use a special interface map that doesn't use eternal handles + // instead of the default V8FunctionTemplateMap. + V8GlobalValueMap + interface_template_map_for_v8_context_snapshot_; + std::unique_ptr string_cache_; std::unique_ptr private_property_; RefPtr script_regexp_script_state_; diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn index a5b93fb2e43ed3..6372a5653e9a4b 100644 --- a/third_party/WebKit/public/BUILD.gn +++ b/third_party/WebKit/public/BUILD.gn @@ -611,6 +611,7 @@ source_set("blink_headers") { "web/WebUserGestureToken.h", "web/WebUserMediaClient.h", "web/WebUserMediaRequest.h", + "web/WebV8ContextSnapshot.h", "web/WebView.h", "web/WebViewClient.h", "web/WebWidget.h", diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h index 2acc45ecce37f5..4b87188a41cf55 100644 --- a/third_party/WebKit/public/platform/Platform.h +++ b/third_party/WebKit/public/platform/Platform.h @@ -701,6 +701,13 @@ class BLINK_PLATFORM_EXPORT Platform { // depending on memory pressure. virtual void RequestPurgeMemory() {} + // V8 Context Snapshot -------------------------------------------------- + + // This method returns true only when + // tools/v8_context_snapshot/v8_context_snapshot_generator is running (which + // runs during Chromium's build step). + virtual bool IsTakingV8ContextSnapshot() { return false; } + // Feature Policy ----------------------------------------------------- // Create a new feature policy object for a document, given its parent diff --git a/third_party/WebKit/public/web/WebV8ContextSnapshot.h b/third_party/WebKit/public/web/WebV8ContextSnapshot.h new file mode 100644 index 00000000000000..336bedb66310f2 --- /dev/null +++ b/third_party/WebKit/public/web/WebV8ContextSnapshot.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WebV8ContextSnapshot_h +#define WebV8ContextSnapshot_h + +#include "public/platform/WebCommon.h" +#include "v8/include/v8.h" + +namespace blink { + +// WebV8ContextSnapshot is an API to take a snapshot of V8 context. +// This API should be used only by tools/v8_context_snapshot, which runs during +// Chromium's build step. +class BLINK_EXPORT WebV8ContextSnapshot { + public: + static v8::StartupData TakeSnapshot(); +}; + +} // namespace blink + +#endif // WebV8ContextSnapshot_h diff --git a/tools/v8_context_snapshot/BUILD.gn b/tools/v8_context_snapshot/BUILD.gn new file mode 100644 index 00000000000000..57c22e481eaef6 --- /dev/null +++ b/tools/v8_context_snapshot/BUILD.gn @@ -0,0 +1,97 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Targets in ths file are to take a V8 context snapshot on build time. +# Created V8 context snapshot is used in +# third_party/WebKit/Source/bindings/core/v8/V8ContextSnapshot.{cpp|h}. +# to speedup creating a V8 context and setting up around it. + +import("//build/config/c++/c++.gni") +import("//v8/snapshot_toolchain.gni") + +if (is_android) { + import("//build/config/android/rules.gni") +} + +if (is_android) { + android_assets("v8_context_snapshot_assets") { + deps = [ + ":v8_context_snapshot", + ] + sources = [ + "$root_out_dir/v8_context_snapshot.bin", + ] + disable_compression = true + } +} + +group("v8_context_snapshot") { + if (!is_chromeos) { + public_deps = [ + ":generate_v8_context_snapshot", + ] + } +} + +if (!is_chromeos && !is_android) { + action("generate_v8_context_snapshot") { + script = "run.py" + output_file = "$root_out_dir/v8_context_snapshot.bin" + output_path = rebase_path(output_file, root_build_dir) + + args = [ + "./" + rebase_path( + get_label_info( + ":v8_context_snapshot_generator($v8_snapshot_toolchain)", + "root_out_dir") + "/v8_context_snapshot_generator", + root_build_dir), + "--output_file=$output_path", + ] + + outputs = [ + output_file, + ] + + deps = [ + ":v8_context_snapshot_generator($v8_snapshot_toolchain)", + ] + } + + # This config disables a link time optimization "ICF", which may merge different + # functions into one if the function signature and body of them are identical. + # + # ICF breaks 1:1 mappings of the external references for V8 snapshot, so we + # disable it while taking a V8 snapshot. + config("disable_icf") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + if (is_win) { + ldflags = [ "/OPT:NOICF" ] + } else if (is_posix && !is_mac) { + ldflags = [ "-Wl,--icf=none" ] + } + } + + executable("v8_context_snapshot_generator") { + sources = [ + "v8_context_snapshot_generator.cc", + ] + + deps = [ + "//gin:gin", + "//mojo/edk/system:system", + "//third_party/WebKit/public:blink", + "//v8", + ] + + if (is_linux && (is_component_build || using_sanitizer) && + use_custom_libcxx) { + deps += [ "//buildtools/third_party/libc++:libc++" ] + } + + configs += [ + "//v8:external_startup_data", + ":disable_icf", + ] + } +} diff --git a/tools/v8_context_snapshot/DEPS b/tools/v8_context_snapshot/DEPS new file mode 100644 index 00000000000000..4af288af27c211 --- /dev/null +++ b/tools/v8_context_snapshot/DEPS @@ -0,0 +1,7 @@ +include_rules = [ + "+base", + "+v8", + "+third_party/WebKit/public", + "+gin/v8_initializer.h", + "+mojo/edk/embedder/embedder.h", +] diff --git a/tools/v8_context_snapshot/OWNERS b/tools/v8_context_snapshot/OWNERS new file mode 100644 index 00000000000000..9cf965177f1b7e --- /dev/null +++ b/tools/v8_context_snapshot/OWNERS @@ -0,0 +1,6 @@ +haraken@chromium.org +peria@chromium.org +yukishiino@chromium.org + +# TEAM: blink-reviews-bindings@chromium.org +# COMPONENT: Blink>Bindings diff --git a/tools/v8_context_snapshot/run.py b/tools/v8_context_snapshot/run.py new file mode 100644 index 00000000000000..a376b4c10e8a57 --- /dev/null +++ b/tools/v8_context_snapshot/run.py @@ -0,0 +1,15 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""This program wraps an arbitrary command since gn currently can only execute +scripts.""" + +import os +import subprocess +import sys + +args = sys.argv[1:] +args[0] = os.path.abspath(args[0]) + +sys.exit(subprocess.call(args)) diff --git a/tools/v8_context_snapshot/v8_context_snapshot_generator.cc b/tools/v8_context_snapshot/v8_context_snapshot_generator.cc new file mode 100644 index 00000000000000..449a54633f709d --- /dev/null +++ b/tools/v8_context_snapshot/v8_context_snapshot_generator.cc @@ -0,0 +1,69 @@ +// Copyright 2017 The Chromium 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 "base/at_exit.h" +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/message_loop/message_loop.h" +#include "base/task_scheduler/task_scheduler.h" +#include "gin/v8_initializer.h" +#include "mojo/edk/embedder/embedder.h" +#include "third_party/WebKit/public/platform/WebThread.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebV8ContextSnapshot.h" +#include "v8/include/v8.h" + +namespace { + +class SnapshotThread : public blink::WebThread { + public: + bool IsCurrentThread() const override { return true; } + blink::WebScheduler* Scheduler() const override { return nullptr; } + blink::WebTaskRunner* GetWebTaskRunner() override { return nullptr; } +}; + +class SnapshotPlatform final : public blink::Platform { + public: + bool IsTakingV8ContextSnapshot() override { return true; } + blink::WebThread* CurrentThread() override { + static SnapshotThread dummy_thread; + return &dummy_thread; + } +}; + +} // namespace + +// This program takes a snapshot of V8 contexts and writes it out as a file. +// The snapshot file is consumed by Blink. +// +// Usage: +// % v8_context_snapshot_generator --output_file= +int main(int argc, char** argv) { + base::AtExitManager at_exit; + base::CommandLine::Init(argc, argv); +#ifdef V8_USE_EXTERNAL_STARTUP_DATA + gin::V8Initializer::LoadV8Snapshot(); + gin::V8Initializer::LoadV8Natives(); +#endif + + // Set up environment to make Blink and V8 workable. + base::MessageLoop message_loop; + base::TaskScheduler::CreateAndStartWithDefaultParams("TakeSnapshot"); + mojo::edk::Init(); + + // Take a snapshot. + SnapshotPlatform platform; + blink::Initialize(&platform); + v8::StartupData blob = blink::WebV8ContextSnapshot::TakeSnapshot(); + + // Save the snapshot as a file. Filename is given in a command line option. + base::FilePath file_path = + base::CommandLine::ForCurrentProcess()->GetSwitchValuePath("output_file"); + CHECK(!file_path.empty()); + CHECK_LT(0, base::WriteFile(file_path, blob.data, blob.raw_size)); + + delete[] blob.data; + + return 0; +}