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

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: Work for all comments Created 3 years, 6 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 <cstring>
8
9 #include "bindings/core/v8/GeneratedCodeHelper.h"
10 #include "bindings/core/v8/V8Document.h"
11 #include "bindings/core/v8/V8EventTarget.h"
12 #include "bindings/core/v8/V8HTMLDocument.h"
13 #include "bindings/core/v8/V8Initializer.h"
14 #include "bindings/core/v8/V8Node.h"
15 #include "bindings/core/v8/V8Window.h"
16 #include "platform/bindings/DOMWrapperWorld.h"
17 #include "platform/bindings/V8ObjectConstructor.h"
18 #include "platform/bindings/V8PerIsolateData.h"
19 #include "platform/bindings/V8PrivateProperty.h"
20 #include "v8/include/v8.h"
21
22 namespace blink {
23
24 namespace {
25
26 intptr_t* g_snapshot_reference_table = nullptr;
27
28 // TODO(peria): This method is almost a copy of
29 // V8PerContext::ConstructorForTypeSlowCase(), so merge with it.
30 v8::Local<v8::Function> ConstructPlainType(v8::Isolate* isolate,
31 const DOMWrapperWorld& world,
32 v8::Local<v8::Context> context,
33 const WrapperTypeInfo* type) {
34 v8::Context::Scope scope(context);
35 // We shouldn't reach this point for the types that are implemented in v8 such
36 // as typed arrays and hence don't have domTemplateFunction.
37 DCHECK(type->dom_template_function);
38 v8::Local<v8::FunctionTemplate> interface_template =
39 type->domTemplate(isolate, world);
40 // Getting the function might fail if we're running out of stack or memory.
41 v8::Local<v8::Function> interface_object;
42 if (!interface_template->GetFunction(context).ToLocal(&interface_object))
43 return v8::Local<v8::Function>();
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 CHECK(interface_object->Get(context, V8AtomicString(isolate, "prototype"))
54 .ToLocal(&prototype_value));
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 CHECK(V8ObjectConstructor::NewInstance(isolate, interface_object)
84 .ToLocal(&instance_template));
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,
Yuki 2017/06/20 14:20:11 nit: one entry per line rather than manual spacing
peria 2017/06/21 07:19:16 clang-format does it.
100 &V8EventTarget::wrapperTypeInfo, &V8Node::wrapperTypeInfo,
101 &V8Document::wrapperTypeInfo,
102 };
103
104 enum class InternalFieldType : uint8_t {
105 kNone,
106 kNodeType,
107 kDocumentType,
108 kHTMLDocumentType,
109 kHTMLDocumentObject,
110 };
111
112 const WrapperTypeInfo* FieldTypeToWrapperTypeInfo(InternalFieldType type) {
113 switch (type) {
114 case InternalFieldType::kNone:
115 NOTREACHED();
116 break;
117 case InternalFieldType::kNodeType:
118 return &V8Node::wrapperTypeInfo;
119 case InternalFieldType::kDocumentType:
120 return &V8Document::wrapperTypeInfo;
121 case InternalFieldType::kHTMLDocumentType:
122 return &V8HTMLDocument::wrapperTypeInfo;
123 case InternalFieldType::kHTMLDocumentObject:
124 return &V8HTMLDocument::wrapperTypeInfo;
125 }
126 NOTREACHED();
127 return nullptr;
128 }
129
130 struct DataForDeserializer {
131 STACK_ALLOCATED();
132 Member<Document> document;
133 };
134
135 int CountExternalReferenceEntries() {
136 if (!g_snapshot_reference_table)
137 return 0;
138
139 // To ignore the terminate entry '0'.
Yuki 2017/06/20 14:20:11 It seems that you're not counting the null termina
peria 2017/06/21 07:19:16 Done.
140 int count = -1;
141 for (intptr_t* p = g_snapshot_reference_table; *p; ++p)
142 ++count;
143 return count;
144 }
145
146 } // namespace
147
148 v8::StartupData V8SnapshotUtil::TakeSnapshot() {
149 DCHECK_EQ(V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
150 ->GetV8ContextMode(),
151 V8PerIsolateData::V8ContextMode::kTakeSnapshot);
152
153 v8::SnapshotCreator* creator =
154 V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
155 ->GetSnapshotCreator();
156 v8::Isolate* isolate = creator->GetIsolate();
157 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
158
159 LOG(INFO) << "External reference table has "
160 << CountExternalReferenceEntries() << " entries.";
161
162 // Disable all runtime enabled featuers
163 RuntimeEnabledFeatures::SetStableFeaturesEnabled(false);
164 RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(false);
165 RuntimeEnabledFeatures::SetTestFeaturesEnabled(false);
166
167 {
168 v8::HandleScope handleScope(isolate);
169 creator->SetDefaultContext(v8::Context::New(isolate));
170
171 TakeSnapshotForWorld(creator, DOMWrapperWorld::MainWorld());
172 // For non main worlds, we can use any type to create a context.
173 TakeSnapshotForWorld(creator, *DOMWrapperWorld::EnsureIsolatedWorld(
174 isolate, kWorldIdForNonMainWorld));
175 }
176
177 // Snapshot is taken on the main thread, but it can be used on other threads.
178 // So we remove a message handler for the main thread.
179 isolate->RemoveMessageListeners(V8Initializer::MessageHandlerInMainThread);
180
181 return creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
182 }
183
184 void V8SnapshotUtil::TakeSnapshotForWorld(v8::SnapshotCreator* creator,
185 const DOMWrapperWorld& world) {
186 v8::Isolate* isolate = creator->GetIsolate();
187 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
188
189 // Function templates
190 v8::HandleScope handleScope(isolate);
191 Vector<v8::Local<v8::FunctionTemplate>> interface_templates;
192 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
193 v8::Local<v8::FunctionTemplate> interface_template =
194 wrapper_type_info->domTemplate(isolate, world);
195 CHECK(!interface_template.IsEmpty());
196 interface_templates.push_back(interface_template);
197 }
198
199 // FIXME: Confirm interface_tempaltes[0] is a template of V8Window.
Yuki 2017/06/20 14:20:11 s/FIXME/TODO(peria)/
peria 2017/06/21 07:19:16 Done.
200 v8::Local<v8::ObjectTemplate> window_template =
201 interface_templates[0]->InstanceTemplate();
202 CHECK(!window_template.IsEmpty());
203
204 v8::Local<v8::Context> context;
205 {
206 V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
207 V8PerIsolateData::From(isolate));
208 context = v8::Context::New(isolate, nullptr, window_template);
209 }
210 CHECK(!context.IsEmpty());
211
212 if (world.IsMainWorld()) {
213 v8::Context::Scope scope(context);
214 v8::Local<v8::Object> document_wrapper = CreatePlainWrapper(
215 isolate, world, context, &V8HTMLDocument::wrapperTypeInfo);
216 int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
217 void* values[] = {nullptr, const_cast<WrapperTypeInfo*>(
218 &V8HTMLDocument::wrapperTypeInfo)};
219 document_wrapper->SetAlignedPointerInInternalFields(
220 WTF_ARRAY_LENGTH(indices), indices, values);
221
222 // Update the cached accessor for window.document.
223 CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(isolate).Set(
224 context->Global(), document_wrapper));
225 }
226
227 for (auto& interface_template : interface_templates) {
228 creator->AddTemplate(interface_template);
229 }
230 creator->AddContext(context, SerializeInternalField);
231
232 V8PerIsolateData::From(isolate)->ClearPersistentsForV8Snapshot();
233 }
234
235 void V8SnapshotUtil::EnsureInterfaceTemplates(v8::Isolate* isolate) {
236 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
237 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
238 return;
239 }
240
241 EnsureInterfaceTemplatesForWorld(isolate, DOMWrapperWorld::MainWorld());
242 EnsureInterfaceTemplatesForWorld(
243 isolate,
244 *DOMWrapperWorld::EnsureIsolatedWorld(isolate, kWorldIdForNonMainWorld));
245 }
246
247 void V8SnapshotUtil::EnsureInterfaceTemplatesForWorld(
248 v8::Isolate* isolate,
249 const DOMWrapperWorld& world) {
250 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
251 v8::Local<v8::FunctionTemplate> interface =
252 wrapper_type_info->domTemplate(isolate, world);
253 CHECK(!interface.IsEmpty());
254 }
255 }
256
257 v8::Local<v8::FunctionTemplate> V8SnapshotUtil::InterfaceTemplateFromSnapshot(
258 v8::Isolate* isolate,
259 const DOMWrapperWorld& world,
260 WrapperTypeInfo* wrapper_type_info) {
261 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
262 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
263 return v8::Local<v8::FunctionTemplate>();
264 }
265
266 const int index_offset =
267 world.IsMainWorld() ? 0 : WTF_ARRAY_LENGTH(kSnapshotWrapperTypes);
268
269 // Snapshotted templates are expected to be used just to get
270 // wrapper_type_info.
271 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kSnapshotWrapperTypes); ++i) {
272 if (kSnapshotWrapperTypes[i]->Equals(wrapper_type_info)) {
273 return v8::FunctionTemplate::FromSnapshot(isolate, index_offset + i)
274 .ToLocalChecked();
275 }
276 }
277 return v8::Local<v8::FunctionTemplate>();
278 }
279
280 v8::StartupData V8SnapshotUtil::SerializeInternalField(
281 v8::Local<v8::Object> holder,
282 int index,
283 void*) {
284 InternalFieldType field_type = InternalFieldType::kNone;
285 const WrapperTypeInfo* wrapper_type = ToWrapperTypeInfo(holder);
286 if (kV8DOMWrapperObjectIndex == index) {
287 if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
288 field_type = InternalFieldType::kHTMLDocumentObject;
289 }
290 } else if (kV8DOMWrapperTypeIndex == index) {
291 if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
292 field_type = InternalFieldType::kHTMLDocumentType;
293 } else if (blink::V8Document::wrapperTypeInfo.Equals(wrapper_type)) {
294 field_type = InternalFieldType::kDocumentType;
295 } else if (blink::V8Node::wrapperTypeInfo.Equals(wrapper_type)) {
296 field_type = InternalFieldType::kNodeType;
297 }
298 }
299 CHECK_NE(field_type, InternalFieldType::kNone);
300
301 int size = sizeof(InternalFieldType);
302 char* data = new char[size];
303 std::memcpy(data, &field_type, size);
304
305 return {data, size};
306 }
307
308 void V8SnapshotUtil::DeserializeInternalField(v8::Local<v8::Object> holder,
309 int index,
310 v8::StartupData payload,
311 void* ptr) {
312 // DeserializeInternalField() expects to be called in the main world
313 // with |document| being HTMLDocument.
314 CHECK_EQ(payload.raw_size, static_cast<int>(sizeof(InternalFieldType)));
315 InternalFieldType type =
316 *reinterpret_cast<const InternalFieldType*>(payload.data);
317
318 const WrapperTypeInfo* wrapper_type_info = FieldTypeToWrapperTypeInfo(type);
319 switch (type) {
320 case InternalFieldType::kNodeType:
321 case InternalFieldType::kDocumentType:
322 case InternalFieldType::kHTMLDocumentType: {
323 CHECK_EQ(index, kV8DOMWrapperTypeIndex);
324 holder->SetAlignedPointerInInternalField(
325 index, const_cast<WrapperTypeInfo*>(wrapper_type_info));
326 return;
327 }
328 case InternalFieldType::kHTMLDocumentObject: {
329 CHECK_EQ(index, kV8DOMWrapperObjectIndex);
330 v8::Isolate* isolate = v8::Isolate::GetCurrent();
331 DataForDeserializer* data = static_cast<DataForDeserializer*>(ptr);
332 ScriptWrappable* document = data->document;
333 DCHECK(document);
334
335 // Make reference from wrapper to document
336 holder->SetAlignedPointerInInternalField(index, document);
337 // Make reference from document to wrapper
338 CHECK(document->SetWrapper(isolate, wrapper_type_info, holder));
339 WrapperTypeInfo::WrapperCreated();
340 return;
341 }
342 case InternalFieldType::kNone:
343 NOTREACHED();
344 return;
345 }
346
347 NOTREACHED();
348 }
349
350 v8::Local<v8::Context> V8SnapshotUtil::CreateContext(
351 v8::Isolate* isolate,
352 const DOMWrapperWorld& world,
353 v8::ExtensionConfiguration* extension_configuration,
354 v8::Local<v8::Object> global_proxy,
355 Document* document) {
356 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
357 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
358 return v8::Local<v8::Context>();
359 }
360 // For main world frames, a HTMLDocument object is required to be
361 // deserialized.
362 if (world.IsMainWorld() && !(document && document->IsHTMLDocument())) {
363 return v8::Local<v8::Context>();
364 }
365 const int index = GetSnapshotIndexForWorld(world);
366 DataForDeserializer data{document};
367 v8::DeserializeInternalFieldsCallback callback =
368 v8::DeserializeInternalFieldsCallback(&DeserializeInternalField, &data);
369 v8::Local<v8::Context> context =
370 v8::Context::FromSnapshot(isolate, index, callback,
371 extension_configuration, global_proxy)
372 .ToLocalChecked();
373 CHECK(!context.IsEmpty());
374
375 if (world.IsMainWorld()) {
376 v8::Context::Scope scope(context);
377 v8::Local<v8::Object> wrapper = document->MainWorldWrapper(isolate);
378 CHECK(!wrapper.IsEmpty());
379 v8::Local<v8::Object> empty_prototype;
380 v8::Local<v8::Function> empty_interface;
381
382 V8HTMLDocument::InstallRuntimeEnabledFeatures(
383 isolate, world, wrapper, empty_prototype, empty_interface);
384 V8Document::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
385 empty_prototype, empty_interface);
386 V8Node::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
387 empty_prototype, empty_interface);
388 V8EventTarget::InstallRuntimeEnabledFeatures(
389 isolate, world, wrapper, empty_prototype, empty_interface);
390 }
391 return context;
392 }
393
394 void V8SnapshotUtil::SetupContext(v8::Local<v8::Context> context) {
395 ScriptState* script_state = ScriptState::From(context);
396 v8::Isolate* isolate = script_state->GetIsolate();
397 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
398 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
399 return;
400 }
401
402 DOMWrapperWorld& world = script_state->World();
403 V8PerContextData* data = script_state->PerContextData();
404
405 v8::Local<v8::Object> empty_prototype;
406 v8::Local<v8::Function> empty_interface;
407 // Set up Window wrapper.
408 {
409 v8::Local<v8::Object> global_proxy = context->Global();
410 v8::Local<v8::Object> wrapper =
411 global_proxy->GetPrototype().As<v8::Object>();
412 V8Window::installV8WindowRuntimeEnabledFunction(
413 isolate, world, wrapper, empty_prototype, empty_interface);
414 V8EventTarget::InstallRuntimeEnabledFeatures(
415 isolate, world, wrapper, empty_prototype, empty_interface);
416 }
417
418 // Setup V8PerContextData::constructor_map_ and wrapper_boilerplate_.
419 {
420 const WrapperTypeInfo* type = &V8EventTarget::wrapperTypeInfo;
421 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
422 v8::Local<v8::Object> prototype =
423 constructor->Get(context, V8AtomicString(isolate, "prototype"))
424 .ToLocalChecked()
425 .As<v8::Object>();
426 v8::Local<v8::Object> wrapper = data->CreateWrapperFromCache(type);
427 V8EventTarget::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
428 prototype, constructor);
429 data->constructor_map_.Set(type, constructor);
430 data->wrapper_boilerplates_.Set(type, wrapper);
431 }
432 {
433 const WrapperTypeInfo* type = &V8Window::wrapperTypeInfo;
434 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
435 v8::Local<v8::Object> prototype =
436 constructor->Get(context, V8AtomicString(isolate, "prototype"))
437 .ToLocalChecked()
438 .As<v8::Object>();
439 v8::Local<v8::Object> wrapper = data->CreateWrapperFromCache(type);
440 V8Window::installV8WindowRuntimeEnabledFunction(isolate, world, wrapper,
441 prototype, constructor);
442 V8EventTarget::InstallRuntimeEnabledFeatures(
443 isolate, world, wrapper, empty_prototype, empty_interface);
444 data->constructor_map_.Set(type, constructor);
445 data->wrapper_boilerplates_.Set(type, wrapper);
446 }
447 {
448 const WrapperTypeInfo* type = &V8Node::wrapperTypeInfo;
449 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
450 v8::Local<v8::Object> prototype =
451 constructor->Get(context, V8AtomicString(isolate, "prototype"))
452 .ToLocalChecked()
453 .As<v8::Object>();
454 v8::Local<v8::Object> wrapper = data->CreateWrapperFromCache(type);
455 V8Node::InstallRuntimeEnabledFeatures(isolate, world, wrapper, prototype,
456 constructor);
457 V8EventTarget::InstallRuntimeEnabledFeatures(
458 isolate, world, wrapper, empty_prototype, empty_interface);
459 data->constructor_map_.Set(type, constructor);
460 data->wrapper_boilerplates_.Set(type, wrapper);
461 }
462 {
463 const WrapperTypeInfo* type = &V8Document::wrapperTypeInfo;
464 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
465 v8::Local<v8::Object> prototype =
466 constructor->Get(context, V8AtomicString(isolate, "prototype"))
467 .ToLocalChecked()
468 .As<v8::Object>();
469 v8::Local<v8::Object> wrapper = data->CreateWrapperFromCache(type);
470 V8Document::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
471 prototype, constructor);
472 V8Node::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
473 empty_prototype, empty_interface);
474 V8EventTarget::InstallRuntimeEnabledFeatures(
475 isolate, world, wrapper, empty_prototype, empty_interface);
476 data->constructor_map_.Set(type, constructor);
477 data->wrapper_boilerplates_.Set(type, wrapper);
478 }
479 {
480 const WrapperTypeInfo* type = &V8HTMLDocument::wrapperTypeInfo;
481 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
482 v8::Local<v8::Object> prototype =
483 constructor->Get(context, V8AtomicString(isolate, "prototype"))
484 .ToLocalChecked()
485 .As<v8::Object>();
486 v8::Local<v8::Object> wrapper = data->CreateWrapperFromCache(type);
487 V8HTMLDocument::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
488 prototype, constructor);
489 V8Document::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
490 empty_prototype, empty_interface);
491 V8Node::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
492 empty_prototype, empty_interface);
493 V8EventTarget::InstallRuntimeEnabledFeatures(
494 isolate, world, wrapper, empty_prototype, empty_interface);
495 data->constructor_map_.Set(type, constructor);
496 data->wrapper_boilerplates_.Set(type, wrapper);
497 }
498 }
499
500 void V8SnapshotUtil::SetReferenceTable(intptr_t* table) {
501 DCHECK(!g_snapshot_reference_table);
502 g_snapshot_reference_table = table;
503 }
504
505 intptr_t* V8SnapshotUtil::GetReferenceTable() {
506 DCHECK(g_snapshot_reference_table);
507 return g_snapshot_reference_table;
508 }
509
510 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698