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

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

Issue 2841443005: [Bindings] Create and use V8 context snapshots (Closed)
Patch Set: Work for most 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/V8SnapshotCreator.h"
6
7 #include <cstring>
8
9 #include "bindings/core/v8/GeneratedCodeHelper.h"
10 #include "bindings/core/v8/V8Document.h"
Yuki 2017/05/30 14:35:57 V8Document.h and V8Node.h are not necessary here.
peria 2017/06/01 08:33:33 Done.
11 #include "bindings/core/v8/V8HTMLDocument.h"
12 #include "bindings/core/v8/V8Initializer.h"
13 #include "bindings/core/v8/V8Node.h"
14 #include "bindings/core/v8/V8Window.h"
15 #include "platform/bindings/DOMWrapperWorld.h"
16 #include "platform/bindings/V8ObjectConstructor.h"
17 #include "platform/bindings/V8PerIsolateData.h"
18 #include "platform/bindings/V8PrivateProperty.h"
19 #include "v8/include/v8.h"
20
21 namespace blink {
22
23 namespace {
24
25 intptr_t* g_snapshot_reference_table = nullptr;
26
27 // NOTE(peria): This method is almost a copy of
28 // V8PerContext::ConstructorForTypeSlowCase().
29 // TODO(peria): 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 // NOTE(peria): This method is almost a copy of
71 // V8PerContext::CreateWrapperFromCacheSlowCase().
72 // TODO(peria): Merge with it.
73 v8::Local<v8::Object> CreatePlainWrapper(v8::Isolate* isolate,
74 const DOMWrapperWorld& world,
75 v8::Local<v8::Context> context,
76 const WrapperTypeInfo* type) {
77 CHECK(V8HTMLDocument::wrapperTypeInfo.Equals(type));
78
79 v8::Context::Scope scope(context);
80 v8::Local<v8::Function> interface_object =
81 ConstructPlainType(isolate, world, context, type);
82 CHECK(!interface_object.IsEmpty());
83 v8::Local<v8::Object> instance_template;
84 CHECK(V8ObjectConstructor::NewInstance(isolate, interface_object)
85 .ToLocal(&instance_template));
86 v8::Local<v8::Object> wrapper = instance_template->Clone();
87 wrapper->SetAlignedPointerInInternalField(kV8DOMWrapperTypeIndex,
88 const_cast<WrapperTypeInfo*>(type));
89 return wrapper;
90 }
91
92 int GetSnapshotIndexForWorld(const DOMWrapperWorld& world) {
93 return world.IsMainWorld() ? 0 : 1;
94 }
95
96 constexpr const WrapperTypeInfo* kSnapshotWrapperTypes[] = {
97 &V8EventTarget::wrapperTypeInfo, &V8Window::wrapperTypeInfo,
Yuki 2017/05/30 14:35:57 nit: Let's put them one-entry-per-line. Why don't
peria 2017/06/01 08:33:33 clang-format does it.
98 &V8Node::wrapperTypeInfo, &V8Document::wrapperTypeInfo,
99 &V8HTMLDocument::wrapperTypeInfo,
100 };
101
102 enum class InternalFieldType : uint8_t {
103 kNone,
104 kNodeType,
105 kDocumentType,
106 kHTMLDocumentType,
107 kHTMLDocumentObject,
108 };
109
110 const WrapperTypeInfo* FieldTypeToWrapperTypeInfo(InternalFieldType type) {
111 switch (type) {
112 case InternalFieldType::kNodeType:
113 return &V8Node::wrapperTypeInfo;
114 case InternalFieldType::kDocumentType:
115 return &V8Document::wrapperTypeInfo;
116 case InternalFieldType::kHTMLDocumentType:
117 return &V8HTMLDocument::wrapperTypeInfo;
118 case InternalFieldType::kHTMLDocumentObject:
119 return &V8HTMLDocument::wrapperTypeInfo;
120 case InternalFieldType::kNone:
Yuki 2017/05/30 14:35:57 nit: The same order of the type definition is pref
peria 2017/06/01 08:33:33 Done.
121 NOTREACHED();
122 break;
123 }
124 NOTREACHED();
125 return nullptr;
126 }
127
128 struct DataForDeserializer {
129 STACK_ALLOCATED();
130 Member<Document> document;
131 };
132
133 } // namespace
134
135 void V8SnapshotCreator::TakeSnapshotForWorld(v8::SnapshotCreator* creator,
Yuki 2017/05/30 14:35:57 nit: The style guide recommends the same order of
peria 2017/06/01 08:33:33 Done.
136 const DOMWrapperWorld& world) {
137 v8::Isolate* isolate = creator->GetIsolate();
138 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
139
140 // Function templates
141 v8::HandleScope handleScope(isolate);
142 Vector<v8::Local<v8::FunctionTemplate>> interface_templates;
143 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
144 v8::Local<v8::FunctionTemplate> interface_template =
145 wrapper_type_info->domTemplate(isolate, world);
146 CHECK(!interface_template.IsEmpty());
147 interface_templates.push_back(interface_template);
148 }
149
150 // FIXME: Confirm interface_tempaltes[1] is a template of V8Window.
151 v8::Local<v8::ObjectTemplate> window_template =
152 interface_templates[1]->InstanceTemplate();
153 CHECK(!window_template.IsEmpty());
154
155 v8::Local<v8::Context> context;
156 {
157 V8PerIsolateData::UseCounterDisabledScope use_counter_disabled(
158 V8PerIsolateData::From(isolate));
159 context = v8::Context::New(isolate, nullptr, window_template);
160 }
161 CHECK(!context.IsEmpty());
162
163 if (world.IsMainWorld()) {
164 context->Enter();
Yuki 2017/05/30 14:35:56 v8::Context::Scope enters into the context. You do
peria 2017/06/01 08:33:33 Done.
165 v8::Context::Scope scope(context);
166 v8::Local<v8::Object> document_wrapper = CreatePlainWrapper(
167 isolate, world, context, &V8HTMLDocument::wrapperTypeInfo);
168 int indices[] = {kV8DOMWrapperObjectIndex, kV8DOMWrapperTypeIndex};
169 void* values[] = {nullptr, const_cast<WrapperTypeInfo*>(
170 &V8HTMLDocument::wrapperTypeInfo)};
171 document_wrapper->SetAlignedPointerInInternalFields(
172 WTF_ARRAY_LENGTH(indices), indices, values);
173
174 // Update the cached accessor for window.document.
175 CHECK(V8PrivateProperty::GetWindowDocumentCachedAccessor(isolate).Set(
176 context->Global(), document_wrapper));
177 context->Exit();
Yuki 2017/05/30 14:35:56 Ditto. The Scope automatically exits.
peria 2017/06/01 08:33:33 Acknowledged.
178 }
179
180 for (auto& interface_template : interface_templates) {
181 creator->AddTemplate(interface_template);
182 }
183 creator->AddContext(context, SerializeInternalField);
184
185 V8PerIsolateData::From(isolate)->ClearPersistents();
186 }
187
188 v8::StartupData V8SnapshotCreator::TakeSnapshot() {
189 v8::SnapshotCreator* creator =
190 V8PerIsolateData::From(V8PerIsolateData::MainThreadIsolate())
191 ->GetSnapshotCreator();
192 v8::Isolate* isolate = creator->GetIsolate();
193 CHECK_EQ(isolate, v8::Isolate::GetCurrent());
194
195 // Disable all runtime enabled featuers
196 RuntimeEnabledFeatures::setStableFeaturesEnabled(false);
197 RuntimeEnabledFeatures::setExperimentalFeaturesEnabled(false);
198 RuntimeEnabledFeatures::setTestFeaturesEnabled(false);
199
200 {
201 v8::HandleScope handleScope(isolate);
202 creator->SetDefaultContext(v8::Context::New(isolate));
203
204 TakeSnapshotForWorld(creator, DOMWrapperWorld::MainWorld());
205 // For non main worlds, we can use any type to create a context.
206 TakeSnapshotForWorld(creator,
207 *DOMWrapperWorld::Create(
208 isolate, DOMWrapperWorld::WorldType::kTesting));
Yuki 2017/05/30 14:35:57 kTesting looks too weird, although I understand th
peria 2017/06/01 08:33:33 Done.
209 }
210
211 // Snapshot is taken on the main thread, but it can be used on other threads.
212 // So we remove a message handler for the main thread.
213 isolate->RemoveMessageListeners(V8Initializer::MessageHandlerInMainThread);
214
215 return creator->CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
216 }
217
218 void V8SnapshotCreator::EnsureInterfaceTemplates(v8::Isolate* isolate,
219 const DOMWrapperWorld& world) {
220 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
221 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
222 return;
223 }
224
225 // TODO(peria): Skip this method if all templates are already created.
226 for (const WrapperTypeInfo* wrapper_type_info : kSnapshotWrapperTypes) {
227 v8::Local<v8::FunctionTemplate> interface =
228 wrapper_type_info->domTemplate(isolate, world);
229 CHECK(!interface.IsEmpty());
230 }
231 }
232
233 v8::Local<v8::FunctionTemplate> V8SnapshotCreator::CreateInterfaceTemplate(
234 v8::Isolate* isolate,
Yuki 2017/05/30 14:35:57 nit: You can take a V8PerIsolateData* instead of v
peria 2017/06/01 08:33:33 Acknowledged.
235 const DOMWrapperWorld& world,
236 WrapperTypeInfo* wrapper_type_info) {
237 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
238 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
239 return v8::Local<v8::FunctionTemplate>();
240 }
241
242 const int index_offset =
243 world.IsMainWorld() ? 0 : WTF_ARRAY_LENGTH(kSnapshotWrapperTypes);
244
245 // Snapshotted templates are expected to be used just to get
246 // wrapper_type_info.
247 for (size_t i = 0; i < WTF_ARRAY_LENGTH(kSnapshotWrapperTypes); ++i) {
248 if (kSnapshotWrapperTypes[i]->Equals(wrapper_type_info)) {
249 return v8::FunctionTemplate::FromSnapshot(isolate, index_offset + i)
250 .ToLocalChecked();
251 }
252 }
253 return v8::Local<v8::FunctionTemplate>();
254 }
255
256 v8::StartupData V8SnapshotCreator::SerializeInternalField(
257 v8::Local<v8::Object> holder,
258 int index,
259 void* /*data*/) {
Yuki 2017/05/30 14:35:57 You define another |data| in the body of the funct
peria 2017/06/01 08:33:33 Done.
260 InternalFieldType field_type = InternalFieldType::kNone;
261 const WrapperTypeInfo* wrapper_type = ToWrapperTypeInfo(holder);
262 if (kV8DOMWrapperObjectIndex == index) {
263 if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
264 field_type = InternalFieldType::kHTMLDocumentObject;
265 }
266 } else if (kV8DOMWrapperTypeIndex == index) {
267 if (blink::V8HTMLDocument::wrapperTypeInfo.Equals(wrapper_type)) {
268 field_type = InternalFieldType::kHTMLDocumentType;
269 } else if (blink::V8Document::wrapperTypeInfo.Equals(wrapper_type)) {
270 field_type = InternalFieldType::kDocumentType;
271 } else if (blink::V8Node::wrapperTypeInfo.Equals(wrapper_type)) {
272 field_type = InternalFieldType::kNodeType;
273 }
274 }
275
276 // To confirm covering all patterns to be serialized.
Yuki 2017/05/30 14:35:57 nit: I guess that this comment is obvious? Probab
peria 2017/06/01 08:33:33 Done.
277 CHECK_NE(field_type, InternalFieldType::kNone);
278
279 int size = sizeof(InternalFieldType);
280 char* data = new char[size];
281 std::memcpy(data, &field_type, size);
282
283 return {data, size};
284 }
285
286 void V8SnapshotCreator::DeserializeInternalField(v8::Local<v8::Object> holder,
287 int index,
288 v8::StartupData payload,
289 void* ptr) {
290 CHECK_EQ(payload.raw_size, static_cast<int>(sizeof(InternalFieldType)));
291 InternalFieldType type =
292 *reinterpret_cast<const InternalFieldType*>(payload.data);
293
294 const WrapperTypeInfo* wrapper_type_info = FieldTypeToWrapperTypeInfo(type);
295 switch (type) {
296 case InternalFieldType::kNodeType:
297 case InternalFieldType::kDocumentType:
298 case InternalFieldType::kHTMLDocumentType: {
299 CHECK_EQ(index, kV8DOMWrapperTypeIndex);
300 holder->SetAlignedPointerInInternalField(
301 index, const_cast<WrapperTypeInfo*>(wrapper_type_info));
302 return;
303 }
304 case InternalFieldType::kHTMLDocumentObject: {
305 CHECK_EQ(index, kV8DOMWrapperObjectIndex);
306 v8::Isolate* isolate = v8::Isolate::GetCurrent();
307 DataForDeserializer* data = static_cast<DataForDeserializer*>(ptr);
308 ScriptWrappable* document = data->document;
309 DCHECK(document);
310
311 // Make reference from wrapper to document
312 holder->SetAlignedPointerInInternalField(index, document);
313 // Make reference from document to wrapper
314 CHECK(document->SetWrapper(isolate, wrapper_type_info, holder));
Yuki 2017/05/30 14:35:57 WrapperTypeInfo::WrapperCreated() should be called
Yuki 2017/05/30 14:35:57 Add a comment that this only works in the main wor
peria 2017/06/01 08:33:33 Done.
peria 2017/06/01 08:33:33 Done.
315 return;
316 }
317 case InternalFieldType::kNone:
318 NOTREACHED();
319 return;
320 }
321
322 NOTREACHED();
323 }
324
325 v8::Local<v8::Context> V8SnapshotCreator::CreateContext(
326 v8::Isolate* isolate,
327 const DOMWrapperWorld& world,
328 v8::ExtensionConfiguration* extension_configuration,
329 v8::Local<v8::Object> global_proxy,
330 Document* document) {
331 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
332 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
333 return v8::Local<v8::Context>();
334 }
335 // For main world frames, a HTMLDocument object is required to be
336 // deserialized.
337 if (world.IsMainWorld() && !(document && document->IsHTMLDocument())) {
338 return v8::Local<v8::Context>();
Yuki 2017/05/30 14:35:57 Maybe NOTREACHED()?
peria 2017/06/01 08:33:33 No. For example, loading XML files comes here. (w/
339 }
340 v8::Local<v8::Context> context;
341 const int index = GetSnapshotIndexForWorld(world);
342 DataForDeserializer data{document};
343 v8::DeserializeInternalFieldsCallback callback =
344 v8::DeserializeInternalFieldsCallback(&DeserializeInternalField, &data);
345 CHECK(v8::Context::FromSnapshot(isolate, index, callback,
346 extension_configuration, global_proxy)
347 .ToLocal(&context));
Yuki 2017/05/30 14:35:57 ToLocalChecked() instead of CHECK()?
peria 2017/06/01 08:33:33 Done.
348
349 return context;
350 }
351
352 void V8SnapshotCreator::InstallRuntimeEnabledFeaturesOnDocument(
353 ScriptState* script_state,
354 v8::Local<v8::Object> wrapper) {
355 v8::Isolate* isolate = script_state->GetIsolate();
356 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
357 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
358 return;
359 }
360
361 v8::Local<v8::Context> context = script_state->GetContext();
362 DOMWrapperWorld& world = script_state->World();
363
364 v8::Local<v8::Object> htmldocument_prototype =
365 wrapper->GetPrototype().As<v8::Object>();
366 v8::Local<v8::Function> htmldocument_interface =
367 V8HTMLDocument::domTemplate(isolate, world)->GetFunction();
368 V8HTMLDocument::installRuntimeEnabledFeatures(
369 isolate, world, wrapper, htmldocument_prototype, htmldocument_interface);
370
371 v8::Local<v8::Object> document_prototype =
372 htmldocument_prototype->GetPrototype().As<v8::Object>();
373 v8::Local<v8::Function> document_interface =
374 V8Document::domTemplate(isolate, world)->GetFunction();
375 V8Document::installRuntimeEnabledFeatures(
376 isolate, world, wrapper, document_prototype, document_interface);
377
378 V8Document::preparePrototypeAndInterfaceObject(
379 context, world, document_prototype, document_interface,
380 v8::Local<v8::FunctionTemplate>());
Yuki 2017/05/30 14:35:57 Why there is no Node and EventTarget related code?
peria 2017/06/01 08:33:33 I'll update code generator to make InstallRuntimeE
381 }
382
383 void V8SnapshotCreator::InstallRuntimeEnabledFeaturesOnGlobal(
384 ScriptState* script_state,
385 v8::Local<v8::Object> global) {
386 v8::Isolate* isolate = script_state->GetIsolate();
387 if (V8PerIsolateData::From(isolate)->GetV8ContextMode() !=
388 V8PerIsolateData::V8ContextMode::kUseSnapshot) {
389 return;
390 }
391
392 DOMWrapperWorld& world = script_state->World();
393
394 v8::Local<v8::Object> window_prototype =
395 global->GetPrototype().As<v8::Object>();
396 v8::Local<v8::Function> window_interface =
397 V8Window::domTemplate(isolate, world)->GetFunction();
398 V8Window::installV8WindowRuntimeEnabledFunction(
399 isolate, world, global, window_prototype, window_interface);
Yuki 2017/05/30 14:35:56 Ditto. Why no prepare...? Why no V8EventTarget?
peria 2017/06/01 08:33:33 ditto.
400 }
401
402 void V8SnapshotCreator::SetReferenceTable(intptr_t* table) {
403 g_snapshot_reference_table = table;
Yuki 2017/05/30 14:35:56 Nice to have DCHECK(!g_snapshot_reference_table);
peria 2017/06/01 08:33:33 Done.
404 }
405
406 intptr_t* V8SnapshotCreator::GetReferenceTable() {
407 return g_snapshot_reference_table;
Yuki 2017/05/30 14:35:57 Nice to have DCHECK(g_snapshot_reference_table);
peria 2017/06/01 08:33:33 Done.
408 }
409
410 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698