Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(628)

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/V8SnapshotUtil.cpp

Issue 2841443005: [Bindings] Create and use V8 context snapshots (Closed)
Patch Set: Move snapshot maker to tools/ and work for comments Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "bindings/core/v8/V8SnapshotUtil.h"
6
7 #include <array>
8 #include <cstring>
9
10 #include "bindings/core/v8/GeneratedCodeHelper.h"
11 #include "bindings/core/v8/V8Document.h"
12 #include "bindings/core/v8/V8EventTarget.h"
13 #include "bindings/core/v8/V8HTMLDocument.h"
14 #include "bindings/core/v8/V8Initializer.h"
15 #include "bindings/core/v8/V8Node.h"
16 #include "bindings/core/v8/V8Window.h"
17 #include "platform/bindings/DOMWrapperWorld.h"
18 #include "platform/bindings/V8ObjectConstructor.h"
19 #include "platform/bindings/V8PerIsolateData.h"
20 #include "platform/bindings/V8PrivateProperty.h"
21 #include "v8/include/v8.h"
22
23 namespace blink {
24
25 namespace {
26
27 intptr_t* g_snapshot_reference_table = nullptr;
28
29 // TODO(peria): This method is almost a copy of
30 // V8PerContext::ConstructorForTypeSlowCase(), so merge with it.
31 v8::Local<v8::Function> ConstructPlainType(v8::Isolate* isolate,
32 const DOMWrapperWorld& world,
33 v8::Local<v8::Context> context,
34 const WrapperTypeInfo* type) {
35 v8::Context::Scope scope(context);
36 // We shouldn't reach this point for the types that are implemented in v8 such
37 // as typed arrays and hence don't have domTemplateFunction.
38 DCHECK(type->dom_template_function);
39 v8::Local<v8::FunctionTemplate> interface_template =
40 type->domTemplate(isolate, world);
41 // Getting the function might fail if we're running out of stack or memory.
42 v8::Local<v8::Function> interface_object =
43 interface_template->GetFunction(context).ToLocalChecked();
44
45 if (type->parent_class) {
46 v8::Local<v8::Object> prototype_template =
47 ConstructPlainType(isolate, world, context, type->parent_class);
48 CHECK(interface_object->SetPrototype(context, prototype_template)
49 .ToChecked());
50 }
51
52 v8::Local<v8::Value> prototype_value =
53 interface_object->Get(context, V8AtomicString(isolate, "prototype"))
54 .ToLocalChecked();
55 CHECK(prototype_value->IsObject());
56 v8::Local<v8::Object> prototype_object = prototype_value.As<v8::Object>();
57 if (prototype_object->InternalFieldCount() ==
58 kV8PrototypeInternalFieldcount &&
59 type->wrapper_type_prototype ==
60 WrapperTypeInfo::kWrapperTypeObjectPrototype) {
61 prototype_object->SetAlignedPointerInInternalField(
62 kV8PrototypeTypeIndex, const_cast<WrapperTypeInfo*>(type));
63 }
64 type->PreparePrototypeAndInterfaceObject(
65 context, world, prototype_object, interface_object, interface_template);
66
67 return interface_object;
68 }
69
70 // TODO(peria): This method is almost a copy of
71 // V8PerContext::CreateWrapperFromCacheSlowCase(), so merge with it.
72 v8::Local<v8::Object> CreatePlainWrapper(v8::Isolate* isolate,
73 const DOMWrapperWorld& world,
74 v8::Local<v8::Context> context,
75 const WrapperTypeInfo* type) {
76 CHECK(V8HTMLDocument::wrapperTypeInfo.Equals(type));
77
78 v8::Context::Scope scope(context);
79 v8::Local<v8::Function> interface_object =
80 ConstructPlainType(isolate, world, context, type);
81 CHECK(!interface_object.IsEmpty());
82 v8::Local<v8::Object> instance_template =
83 V8ObjectConstructor::NewInstance(isolate, interface_object)
84 .ToLocalChecked();
85 v8::Local<v8::Object> wrapper = instance_template->Clone();
86 wrapper->SetAlignedPointerInInternalField(kV8DOMWrapperTypeIndex,
87 const_cast<WrapperTypeInfo*>(type));
88 return wrapper;
89 }
90
91 constexpr int kWorldIdForNonMainWorld =
92 DOMWrapperWorld::WorldId::kIsolatedWorldIdLimit - 1;
93
94 int GetSnapshotIndexForWorld(const DOMWrapperWorld& world) {
95 return world.IsMainWorld() ? 0 : 1;
96 }
97
98 constexpr const WrapperTypeInfo* kSnapshotWrapperTypes[] = {
99 &V8Window::wrapperTypeInfo, &V8HTMLDocument::wrapperTypeInfo,
100 &V8EventTarget::wrapperTypeInfo, &V8Node::wrapperTypeInfo,
101 &V8Document::wrapperTypeInfo,
102 };
103 constexpr size_t kSnapshotWrapperSize = WTF_ARRAY_LENGTH(kSnapshotWrapperTypes);
104
105 enum class InternalFieldType : uint8_t {
106 kNone,
107 kNodeType,
108 kDocumentType,
109 kHTMLDocumentType,
110 kHTMLDocumentObject,
111 };
112
113 const WrapperTypeInfo* FieldTypeToWrapperTypeInfo(InternalFieldType type) {
114 switch (type) {
115 case InternalFieldType::kNone:
116 NOTREACHED();
117 break;
118 case InternalFieldType::kNodeType:
119 return &V8Node::wrapperTypeInfo;
120 case InternalFieldType::kDocumentType:
121 return &V8Document::wrapperTypeInfo;
122 case InternalFieldType::kHTMLDocumentType:
123 return &V8HTMLDocument::wrapperTypeInfo;
124 case InternalFieldType::kHTMLDocumentObject:
125 return &V8HTMLDocument::wrapperTypeInfo;
126 }
127 NOTREACHED();
128 return nullptr;
129 }
130
131 struct DataForDeserializer {
132 STACK_ALLOCATED();
133 Member<Document> document;
134 };
135
136 int CountExternalReferenceEntries() {
137 if (!g_snapshot_reference_table)
138 return 0;
139
140 int count = 0;
141 for (intptr_t* p = g_snapshot_reference_table; *p; ++p)
142 ++count;
143 return count;
144 }
145
146 } // namespace
147
148 v8::Local<v8::Context> V8SnapshotUtil::CreateContext(
149 v8::Isolate* isolate,
150 const DOMWrapperWorld& world,
151 v8::ExtensionConfiguration* extension_configuration,
152 v8::Local<v8::Object> global_proxy,
153 Document* document) {
154 if (!CanCreateContextFromSnapshot(isolate, world, document)) {
155 return v8::Local<v8::Context>();
156 }
157
158 const int index = GetSnapshotIndexForWorld(world);
159 DataForDeserializer data{document};
160 v8::DeserializeInternalFieldsCallback callback =
161 v8::DeserializeInternalFieldsCallback(&DeserializeInternalField, &data);
162 v8::Local<v8::Context> context =
163 v8::Context::FromSnapshot(isolate, index, callback,
164 extension_configuration, global_proxy)
165 .ToLocalChecked();
166 CHECK(!context.IsEmpty());
Yuki 2017/06/27 12:43:47 This CHECK is meaningless after ToLocalChecked().
peria 2017/06/28 03:02:43 Done.
167 DLOG(INFO) << "A context is created from snapshot for "
168 << (world.IsMainWorld() ? "" : "non-") << "main world";
169
170 return context;
171 }
172
173 void V8SnapshotUtil::SetupContext(v8::Local<v8::Context> context,
174 Document* document) {
175 ScriptState* script_state = ScriptState::From(context);
176 v8::Isolate* isolate = script_state->GetIsolate();
177 const DOMWrapperWorld& world = script_state->World();
178 if (!CanCreateContextFromSnapshot(isolate, world, document)) {
179 return;
180 }
181
182 v8::Local<v8::String> prototype_str = V8AtomicString(isolate, "prototype");
183 V8PerContextData* data = script_state->PerContextData();
184
185 v8::Local<v8::Object> global_proxy = context->Global();
186 v8::Local<v8::Object> window_wrapper =
187 global_proxy->GetPrototype().As<v8::Object>();
188 {
189 const WrapperTypeInfo* type = &V8EventTarget::wrapperTypeInfo;
190 v8::Local<v8::Function> interface = data->ConstructorForType(type);
191 v8::Local<v8::Object> prototype = interface->Get(context, prototype_str)
192 .ToLocalChecked()
193 .As<v8::Object>();
194 V8EventTarget::InstallRuntimeEnabledFeatures(isolate, world, window_wrapper,
Yuki 2017/06/27 12:43:47 I'm confused again. Exactly what InstallRuntimeEn
peria 2017/06/28 03:02:43 Ah, I think your understanding is correct. No need
195 prototype, interface);
196 }
197 {
198 const WrapperTypeInfo* type = &V8Window::wrapperTypeInfo;
199 v8::Local<v8::Function> interface = data->ConstructorForType(type);
200 v8::Local<v8::Object> prototype = interface->Get(context, prototype_str)
201 .ToLocalChecked()
202 .As<v8::Object>();
203 V8Window::install_runtime_enabled_features_function_(
204 isolate, world, window_wrapper, prototype, interface);
205 }
206
207 if (world.IsMainWorld()) {
Yuki 2017/06/27 12:43:49 nit: Can we do an early-exit?
peria 2017/06/28 03:02:43 Done.
208 CHECK(document);
209 DCHECK(document->IsHTMLDocument());
210 CHECK(document->ContainsWrapper());
211 v8::Local<v8::Object> document_wrapper =
212 document->MainWorldWrapper(isolate);
Yuki 2017/06/27 12:43:47 Is this the only reason that V8SnapshotUtil needs
peria 2017/06/28 03:02:43 Done.
213
214 {
215 // Prototype object and interface object of EventTarget were set up with
216 // Window wrapper.
217 V8EventTarget::InstallRuntimeEnabledFeatures(
218 isolate, world, document_wrapper, v8::Local<v8::Object>(),
219 v8::Local<v8::Function>());
220 }
221 {
222 const WrapperTypeInfo* type = &V8Node::wrapperTypeInfo;
223 v8::Local<v8::Function> interface = data->ConstructorForType(type);
224 v8::Local<v8::Object> prototype = interface->Get(context, prototype_str)
225 .ToLocalChecked()
226 .As<v8::Object>();
227 V8Node::InstallRuntimeEnabledFeatures(isolate, world, document_wrapper,
228 prototype, interface);
229 }
230 {
231 const WrapperTypeInfo* type = &V8Document::wrapperTypeInfo;
232 v8::Local<v8::Function> interface = data->ConstructorForType(type);
233 v8::Local<v8::Object> prototype = interface->Get(context, prototype_str)
234 .ToLocalChecked()
235 .As<v8::Object>();
236 V8Document::InstallRuntimeEnabledFeatures(
237 isolate, world, document_wrapper, prototype, interface);
238 }
239 {
240 const WrapperTypeInfo* type = &V8HTMLDocument::wrapperTypeInfo;
241 v8::Local<v8::Function> interface = data->ConstructorForType(type);
242 v8::Local<v8::Object> prototype = interface->Get(context, prototype_str)
243 .ToLocalChecked()
244 .As<v8::Object>();
245 V8HTMLDocument::InstallRuntimeEnabledFeatures(
246 isolate, world, document_wrapper, prototype, interface);
247 }
248 }
249 }
250
251 void V8SnapshotUtil::EnsureInterfaceTemplates(v8::Isolate* isolate) {
252 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
253 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
254 return;
255 }
256
257 EnsureInterfaceTemplatesForWorld(isolate, DOMWrapperWorld::MainWorld());
258 EnsureInterfaceTemplatesForWorld(
259 isolate,
260 *DOMWrapperWorld::EnsureIsolatedWorld(isolate, kWorldIdForNonMainWorld));
261 }
262
263 void V8SnapshotUtil::SetReferenceTable(intptr_t* table) {
264 DCHECK(!g_snapshot_reference_table);
265 g_snapshot_reference_table = table;
266 }
267
268 intptr_t* V8SnapshotUtil::GetReferenceTable() {
269 DCHECK(g_snapshot_reference_table);
270 return g_snapshot_reference_table;
271 }
272
273 v8::StartupData V8SnapshotUtil::TakeSnapshot() {
274 DCHECK_EQ(V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
275 ->GetV8ContextMode(),
276 V8PerIsolateData::V8ContextMode::kTakeSnapshot);
277
278 v8::SnapshotCreator* creator =
279 V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
280 ->GetSnapshotCreator();
281 v8::Isolate* isolate = creator->GetIsolate();
282 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
283
284 VLOG(1) << "External reference table has " << CountExternalReferenceEntries()
285 << " entries.";
286
287 // Disable all runtime enabled featuers
288 RuntimeEnabledFeatures::SetStableFeaturesEnabled(false);
289 RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(false);
290 RuntimeEnabledFeatures::SetTestFeaturesEnabled(false);
291
292 {
293 v8::HandleScope handleScope(isolate);
294 creator->SetDefaultContext(v8::Context::New(isolate));
295
296 TakeSnapshotForWorld(creator, DOMWrapperWorld::MainWorld());
297 // For non main worlds, we can use any type to create a context.
298 TakeSnapshotForWorld(creator, *DOMWrapperWorld::EnsureIsolatedWorld(
299 isolate, kWorldIdForNonMainWorld));
300 }
301
302 // Snapshot is taken on the main thread, but it can be used on other threads.
303 // So we remove a message handler for the main thread.
304 isolate->RemoveMessageListeners(V8Initializer::MessageHandlerInMainThread);
305
306 return creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
307 }
308
309 v8::StartupData V8SnapshotUtil::SerializeInternalField(
310 v8::Local<v8::Object> object,
311 int index,
312 void*) {
313 InternalFieldType field_type = InternalFieldType::kNone;
314 const WrapperTypeInfo* wrapper_type = ToWrapperTypeInfo(object);
315 if (kV8DOMWrapperObjectIndex == index) {
316 if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
317 field_type = InternalFieldType::kHTMLDocumentObject;
318 }
319 } else if (kV8DOMWrapperTypeIndex == index) {
320 if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
321 field_type = InternalFieldType::kHTMLDocumentType;
322 } else if (blink::V8Document::wrapperTypeInfo.Equals(wrapper_type)) {
323 field_type = InternalFieldType::kDocumentType;
324 } else if (blink::V8Node::wrapperTypeInfo.Equals(wrapper_type)) {
325 field_type = InternalFieldType::kNodeType;
326 }
327 }
328 CHECK_NE(field_type, InternalFieldType::kNone);
329
330 int size = sizeof(InternalFieldType);
331 char* data = new char[size];
332 std::memcpy(data, &field_type, size);
333
334 return {data, size};
335 }
336
337 void V8SnapshotUtil::DeserializeInternalField(v8::Local<v8::Object> object,
338 int index,
339 v8::StartupData payload,
340 void* ptr) {
341 // DeserializeInternalField() expects to be called in the main world
342 // with |document| being HTMLDocument.
343 CHECK_EQ(payload.raw_size, static_cast<int>(sizeof(InternalFieldType)));
344 InternalFieldType type =
345 *reinterpret_cast<const InternalFieldType*>(payload.data);
346
347 const WrapperTypeInfo* wrapper_type_info = FieldTypeToWrapperTypeInfo(type);
348 switch (type) {
349 case InternalFieldType::kNodeType:
350 case InternalFieldType::kDocumentType:
351 case InternalFieldType::kHTMLDocumentType: {
352 CHECK_EQ(index, kV8DOMWrapperTypeIndex);
353 object->SetAlignedPointerInInternalField(
354 index, const_cast<WrapperTypeInfo*>(wrapper_type_info));
355 return;
356 }
357 case InternalFieldType::kHTMLDocumentObject: {
358 LOG(INFO) << "wrapper";
359 CHECK_EQ(index, kV8DOMWrapperObjectIndex);
360 LOG(INFO) << "setting document wrapper";
361 v8::Isolate* isolate = v8::Isolate::GetCurrent();
362 DataForDeserializer* data = static_cast<DataForDeserializer*>(ptr);
363 ScriptWrappable* document = data->document;
364 DCHECK(document);
365
366 // Make reference from wrapper to document
367 object->SetAlignedPointerInInternalField(index, document);
368 // Make reference from document to wrapper
369 CHECK(document->SetWrapper(isolate, wrapper_type_info, object));
370 WrapperTypeInfo::WrapperCreated();
371 return;
372 }
373 case InternalFieldType::kNone:
374 NOTREACHED();
375 return;
376 }
377
378 NOTREACHED();
379 }
380
381 bool V8SnapshotUtil::CanCreateContextFromSnapshot(v8::Isolate* isolate,
382 const DOMWrapperWorld& world,
383 Document* document) {
Yuki 2017/06/27 12:43:47 nit: Can we add DCHECK(document)? If document ==
peria 2017/06/28 03:02:43 Done.
384 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
385 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
386 return false;
387 }
388 // In case we create a context for the main world from snapshot, we also need
389 // a HTMLDocument wrapper.
390 if (world.IsMainWorld() && !(document && document->IsHTMLDocument())) {
Yuki 2017/06/27 12:43:47 nit: A→B is defined as ¬A∨B, hence (!A || B) is on
peria 2017/06/28 03:02:43 Done.
391 return false;
392 }
393 return true;
394 }
395
396 void V8SnapshotUtil::EnsureInterfaceTemplatesForWorld(
397 v8::Isolate* isolate,
398 const DOMWrapperWorld& world) {
399 V8PerIsolateData* data = V8PerIsolateData::From(isolate);
400 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
401 v8::Local<v8::FunctionTemplate> interface =
402 InterfaceTemplateFromSnapshot(isolate, world, wrapper_type_info);
403 CHECK(!interface.IsEmpty());
404 data->SetInterfaceTemplate(world, wrapper_type_info, interface);
405 }
406 }
407
408 v8::Local<v8::FunctionTemplate> V8SnapshotUtil::InterfaceTemplateFromSnapshot(
409 v8::Isolate* isolate,
410 const DOMWrapperWorld& world,
411 const WrapperTypeInfo* wrapper_type_info) {
412 static const InstallRuntimeEnabledFeaturesOnTemplateFunction kInstallers[] = {
Yuki 2017/06/27 12:43:49 Maybe my comment was unclear. I was recommending
peria 2017/06/28 03:02:43 Done.
413 V8Window::install_runtime_enabled_features_on_template_function_,
414 V8HTMLDocument::InstallRuntimeEnabledFeaturesOnTemplate,
415 V8EventTarget::InstallRuntimeEnabledFeaturesOnTemplate,
416 V8Node::InstallRuntimeEnabledFeaturesOnTemplate,
417 V8Document::InstallRuntimeEnabledFeaturesOnTemplate,
418 };
419
420 const int index_offset = world.IsMainWorld() ? 0 : kSnapshotWrapperSize;
421
422 for (size_t i = 0; i < kSnapshotWrapperSize; ++i) {
423 if (kSnapshotWrapperTypes[i]->Equals(wrapper_type_info)) {
424 v8::Local<v8::FunctionTemplate> interface_template =
425 v8::FunctionTemplate::FromSnapshot(isolate, index_offset + i)
426 .ToLocalChecked();
427 kInstallers[i](isolate, world, interface_template);
Yuki 2017/06/27 12:43:49 Just a comment for future work, it's not good to h
peria 2017/06/28 03:02:43 Acknowledged.
428 return interface_template;
429 }
430 }
431
432 NOTREACHED();
433 return v8::Local<v8::FunctionTemplate>();
434 }
435
436 void V8SnapshotUtil::TakeSnapshotForWorld(v8::SnapshotCreator* creator,
437 const DOMWrapperWorld& world) {
438 v8::Isolate* isolate = creator->GetIsolate();
439 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
440
441 // Function templates
442 v8::HandleScope handleScope(isolate);
443 std::array<v8::Local<v8::FunctionTemplate>, kSnapshotWrapperSize>
444 interface_templates;
445 v8::Local<v8::FunctionTemplate> window_template;
446 for (size_t i = 0; i < kSnapshotWrapperSize; ++i) {
447 const WrapperTypeInfo* wrapper_type_info = kSnapshotWrapperTypes[i];
448 v8::Local<v8::FunctionTemplate> interface_template =
449 wrapper_type_info->domTemplate(isolate, world);
450 CHECK(!interface_template.IsEmpty());
451 interface_templates[i] = interface_template;
452 if (V8Window::wrapperTypeInfo.Equals(wrapper_type_info)) {
453 window_template = interface_template;
454 }
455 }
456 CHECK(!window_template.IsEmpty());
457
458 v8::Local<v8::ObjectTemplate> window_instance_template =
459 window_template->InstanceTemplate();
460 CHECK(!window_instance_template.IsEmpty());
461
462 v8::Local<v8::Context> context;
463 {
464 V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
465 V8PerIsolateData::From(isolate));
466 context = v8::Context::New(isolate, nullptr, window_instance_template);
467 }
468 CHECK(!context.IsEmpty());
469
470 if (world.IsMainWorld()) {
471 v8::Context::Scope scope(context);
472 v8::Local<v8::Object> document_wrapper = CreatePlainWrapper(
473 isolate, world, context, &V8HTMLDocument::wrapperTypeInfo);
474 int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
475 void* values[] = {nullptr, const_cast<WrapperTypeInfo*>(
476 &V8HTMLDocument::wrapperTypeInfo)};
477 document_wrapper->SetAlignedPointerInInternalFields(
478 WTF_ARRAY_LENGTH(indices), indices, values);
479
480 // Set the cached accessor for window.document.
481 CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(isolate).Set(
482 context->Global(), document_wrapper));
483 }
484
485 for (auto& interface_template : interface_templates) {
486 creator->AddTemplate(interface_template);
487 }
488 creator->AddContext(context, SerializeInternalField);
489
490 V8PerIsolateData::From(isolate)->ClearPersistentsForV8Snapshot();
491 }
492
493 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/bindings/core/v8/V8SnapshotUtil.h ('k') | third_party/WebKit/Source/bindings/modules/v8/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698