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

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: Support runtime feature on templates 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,
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 int count = 0;
140 for (intptr_t* p = g_snapshot_reference_table; *p; ++p)
141 ++count;
142 return count;
143 }
144
145 } // namespace
146
147 v8::StartupData V8SnapshotUtil::TakeSnapshot() {
148 DCHECK_EQ(V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
149 ->GetV8ContextMode(),
150 V8PerIsolateData::V8ContextMode::kTakeSnapshot);
151
152 v8::SnapshotCreator* creator =
153 V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
154 ->GetSnapshotCreator();
155 v8::Isolate* isolate = creator->GetIsolate();
156 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
157
158 VLOG(1) << "External reference table has " << CountExternalReferenceEntries()
159 << " entries.";
160
161 // Disable all runtime enabled featuers
162 RuntimeEnabledFeatures::SetStableFeaturesEnabled(false);
163 RuntimeEnabledFeatures::SetExperimentalFeaturesEnabled(false);
164 RuntimeEnabledFeatures::SetTestFeaturesEnabled(false);
165
166 {
167 v8::HandleScope handleScope(isolate);
168 creator->SetDefaultContext(v8::Context::New(isolate));
169
170 TakeSnapshotForWorld(creator, DOMWrapperWorld::MainWorld());
171 // For non main worlds, we can use any type to create a context.
172 TakeSnapshotForWorld(creator, *DOMWrapperWorld::EnsureIsolatedWorld(
173 isolate, kWorldIdForNonMainWorld));
174 }
175
176 // Snapshot is taken on the main thread, but it can be used on other threads.
177 // So we remove a message handler for the main thread.
178 isolate->RemoveMessageListeners(V8Initializer::MessageHandlerInMainThread);
179
180 return creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
181 }
182
183 void V8SnapshotUtil::TakeSnapshotForWorld(v8::SnapshotCreator* creator,
184 const DOMWrapperWorld& world) {
185 v8::Isolate* isolate = creator->GetIsolate();
186 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
187
188 // Function templates
189 v8::HandleScope handleScope(isolate);
190 Vector<v8::Local<v8::FunctionTemplate>> interface_templates;
191 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
192 v8::Local<v8::FunctionTemplate> interface_template =
193 wrapper_type_info->domTemplate(isolate, world);
194 CHECK(!interface_template.IsEmpty());
195 interface_templates.push_back(interface_template);
196 }
197
198 // TODO(peria): Confirm interface_tempaltes[0] is a template of V8Window.
199 v8::Local<v8::ObjectTemplate> window_template =
200 interface_templates[0]->InstanceTemplate();
201 CHECK(!window_template.IsEmpty());
202
203 v8::Local<v8::Context> context;
204 {
205 V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
206 V8PerIsolateData::From(isolate));
207 context = v8::Context::New(isolate, nullptr, window_template);
208 }
209 CHECK(!context.IsEmpty());
210
211 if (world.IsMainWorld()) {
212 v8::Context::Scope scope(context);
213 v8::Local<v8::Object> document_wrapper = CreatePlainWrapper(
214 isolate, world, context, &V8HTMLDocument::wrapperTypeInfo);
215 int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
216 void* values[] = {nullptr, const_cast<WrapperTypeInfo*>(
217 &V8HTMLDocument::wrapperTypeInfo)};
218 document_wrapper->SetAlignedPointerInInternalFields(
219 WTF_ARRAY_LENGTH(indices), indices, values);
220
221 // Update the cached accessor for window.document.
222 CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(isolate).Set(
223 context->Global(), document_wrapper));
224 }
225
226 for (auto& interface_template : interface_templates) {
227 creator->AddTemplate(interface_template);
228 }
229 creator->AddContext(context, SerializeInternalField);
230
231 V8PerIsolateData::From(isolate)->ClearPersistentsForV8Snapshot();
232 }
233
234 void V8SnapshotUtil::EnsureInterfaceTemplates(v8::Isolate* isolate) {
235 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
236 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
237 return;
238 }
239
240 EnsureInterfaceTemplatesForWorld(isolate, DOMWrapperWorld::MainWorld());
241 EnsureInterfaceTemplatesForWorld(
242 isolate,
243 *DOMWrapperWorld::EnsureIsolatedWorld(isolate, kWorldIdForNonMainWorld));
244 }
245
246 void V8SnapshotUtil::EnsureInterfaceTemplatesForWorld(
247 v8::Isolate* isolate,
248 const DOMWrapperWorld& world) {
249 V8PerIsolateData* data = V8PerIsolateData::From(isolate);
250 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
251 v8::Local<v8::FunctionTemplate> interface =
252 InterfaceTemplateFromSnapshot(isolate, world, wrapper_type_info);
253 CHECK(!interface.IsEmpty());
254 data->SetInterfaceTemplate(world, wrapper_type_info, interface);
255 }
256 }
257
258 v8::Local<v8::FunctionTemplate> V8SnapshotUtil::InterfaceTemplateFromSnapshot(
259 v8::Isolate* isolate,
260 const DOMWrapperWorld& world,
261 const WrapperTypeInfo* wrapper_type_info) {
262 const int index_offset =
263 world.IsMainWorld() ? 0 : WTF_ARRAY_LENGTH(kSnapshotWrapperTypes);
264
265 // Snapshotted templates are expected to be used just to get
266 // wrapper_type_info.
267 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kSnapshotWrapperTypes); ++i) {
268 if (kSnapshotWrapperTypes[i]->Equals(wrapper_type_info)) {
269 v8::Local<v8::FunctionTemplate> interface_template =
270 v8::FunctionTemplate::FromSnapshot(isolate, index_offset + i)
271 .ToLocalChecked();
272 // TODO(peria): Install runtime enabled features here.
273 return interface_template;
274 }
275 }
276 NOTREACHED();
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())) {
Yuki 2017/06/21 09:23:21 Could you elaborate why it's possible that it's no
peria 2017/06/23 02:22:05 Done.
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 // Window wrapper needs to be set up.
406 v8::Local<v8::Object> global_proxy = context->Global();
407 v8::Local<v8::Object> wrapper = global_proxy->GetPrototype().As<v8::Object>();
Yuki 2017/06/21 09:23:21 I think that s/wrapper/window_wrapper/ makes the f
peria 2017/06/23 02:22:05 Done.
408
409 v8::Local<v8::Object> empty_wrapper;
410 v8::Local<v8::String> prototype_str = V8AtomicString(isolate, "prototype");
411 // Set up prototype objects and interface objects stored in snapshot.
412 {
413 const WrapperTypeInfo* type = &V8EventTarget::wrapperTypeInfo;
414 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
415 v8::Local<v8::Object> prototype = constructor->Get(context, prototype_str)
416 .ToLocalChecked()
417 .As<v8::Object>();
418 V8EventTarget::InstallRuntimeEnabledFeatures(isolate, world, wrapper,
419 prototype, constructor);
420 }
421 {
422 const WrapperTypeInfo* type = &V8Window::wrapperTypeInfo;
423 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
424 v8::Local<v8::Object> prototype = constructor->Get(context, prototype_str)
425 .ToLocalChecked()
426 .As<v8::Object>();
427 V8Window::installV8WindowRuntimeEnabledFunction(isolate, world, wrapper,
428 prototype, constructor);
429 }
430 {
431 const WrapperTypeInfo* type = &V8Node::wrapperTypeInfo;
432 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
433 v8::Local<v8::Object> prototype = constructor->Get(context, prototype_str)
434 .ToLocalChecked()
435 .As<v8::Object>();
436 V8Node::InstallRuntimeEnabledFeatures(isolate, world, empty_wrapper,
437 prototype, constructor);
438 }
439 {
440 const WrapperTypeInfo* type = &V8Document::wrapperTypeInfo;
441 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
442 v8::Local<v8::Object> prototype = constructor->Get(context, prototype_str)
443 .ToLocalChecked()
444 .As<v8::Object>();
445 V8Document::InstallRuntimeEnabledFeatures(isolate, world, empty_wrapper,
446 prototype, constructor);
447 }
448 {
449 const WrapperTypeInfo* type = &V8HTMLDocument::wrapperTypeInfo;
450 v8::Local<v8::Function> constructor = data->ConstructorForType(type);
451 v8::Local<v8::Object> prototype = constructor->Get(context, prototype_str)
452 .ToLocalChecked()
453 .As<v8::Object>();
454 V8HTMLDocument::InstallRuntimeEnabledFeatures(isolate, world, empty_wrapper,
455 prototype, constructor);
456 }
457 }
458
459 void V8SnapshotUtil::SetReferenceTable(intptr_t* table) {
460 DCHECK(!g_snapshot_reference_table);
461 g_snapshot_reference_table = table;
462 }
463
464 intptr_t* V8SnapshotUtil::GetReferenceTable() {
465 DCHECK(g_snapshot_reference_table);
466 return g_snapshot_reference_table;
467 }
468
469 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698