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

Side by Side Diff: third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp

Issue 2799323002: [local; kouhei 2] kouhei's giant CL https://codereview.chromium.org/2555653002 (Closed)
Patch Set: Rebase Created 3 years, 8 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 "core/loader/modulescript/ModuleTreeLinker.h"
6
7 #include "bindings/core/v8/ScriptModule.h"
8 #include "core/dom/AncestorList.h"
9 #include "core/dom/ModuleScript.h"
10 #include "core/loader/modulescript/ModuleScriptFetchRequest.h"
11 #include "core/loader/modulescript/ModuleTreeLinkerRegistry.h"
12 #include "wtf/HashSet.h"
13
14 namespace blink {
15
16 ModuleTreeLinker* ModuleTreeLinker::Fetch(
17 const ModuleScriptFetchRequest& request,
18 const AncestorList& ancestor_list,
19 ModuleGraphLevel level,
20 Modulator* modulator,
21 ModuleTreeLinkerRegistry* registry,
22 ModuleTreeClient* client) {
23 AncestorList ancestor_list_with_url = ancestor_list;
24 ancestor_list_with_url.insert(request.Url());
25
26 ModuleTreeLinker* fetcher =
27 new ModuleTreeLinker(ancestor_list_with_url, modulator, registry, client);
28 fetcher->FetchSelf(request, level);
29 return fetcher;
30 }
31
32 ModuleTreeLinker::ModuleTreeLinker(const AncestorList& ancestor_list_with_url,
33 Modulator* modulator,
34 ModuleTreeLinkerRegistry* registry,
35 ModuleTreeClient* client)
36 : modulator_(modulator),
37 registry_(registry),
38 client_(client),
39 ancestor_list_with_url_(ancestor_list_with_url) {
40 CHECK(modulator);
41 CHECK(registry);
42 CHECK(client);
43 }
44
45 DEFINE_TRACE(ModuleTreeLinker) {
46 visitor->Trace(modulator_);
47 visitor->Trace(registry_);
48 visitor->Trace(client_);
49 visitor->Trace(module_script_);
50 visitor->Trace(dependency_clients_);
51 SingleModuleClient::Trace(visitor);
52 }
53
54 void ModuleTreeLinker::AdvanceState(State new_state) {
55 #ifndef NDEBUG
56 printf("mtf %p adv state: %d\n", this, new_state);
57 #endif
58 CHECK_EQ(num_incomplete_descendants_, 0u);
59 switch (state_) {
60 case State::kInitial:
61 CHECK_EQ(new_state, State::kFetchingSelf);
62 break;
63 case State::kFetchingSelf:
64 CHECK(new_state == State::kFetchingDependencies ||
65 new_state == State::kFinished);
66 break;
67 case State::kFetchingDependencies:
68 CHECK(new_state == State::kInstantiating ||
69 new_state == State::kFinished);
70 break;
71 case State::kInstantiating:
72 CHECK_EQ(new_state, State::kFinished);
73 break;
74 case State::kFinished:
75 NOTREACHED();
76 break;
77 default:
78 NOTREACHED();
79 }
80
81 state_ = new_state;
82
83 if (state_ == State::kFinished) {
84 registry_->ReleaseFinishedFetcher(this);
85 client_->NotifyModuleTreeLoadFinished(module_script_);
86 }
87 }
88
89 void ModuleTreeLinker::FetchSelf(const ModuleScriptFetchRequest& request,
90 ModuleGraphLevel level) {
91 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
92
93 // Step 1. Fetch a single module script given url, fetch client settings
94 // object, destination, cryptographic nonce, parser state, credentials mode,
95 // module map settings object, referrer, and the top-level module fetch flag.
96 // If the caller of this algorithm specified custom perform the fetch steps,
97 // pass those along while fetching a single module script.
98 AdvanceState(State::kFetchingSelf);
99 modulator_->FetchSingle(request, level, this);
100
101 // Step 2. Return from this algorithm, and run the following steps when
102 // fetching a single module script asynchronously completes with result.
103 // Note: Modulator::fetchSingle asynchronously notifies result to
104 // ModuleTreeLinker::notifyModuleLoadFinished().
105 }
106
107 void ModuleTreeLinker::NotifyModuleLoadFinished(ModuleScript* module_script) {
108 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
109
110 // Step 3. If result is null, asynchronously complete this algorithm with null
111 // and abort these steps.
112 if (!module_script) {
113 AdvanceState(State::kFinished);
114 return;
115 }
116
117 // Step 4. Otherwise, result is a module script. Fetch the descendants of
118 // result given destination and an ancestor list obtained by appending url to
119 // ancestor list. Wait for fetching the descendants of a module script to
120 // asynchronously complete with descendants result before proceeding to the
121 // next step.
122 module_script_ = module_script;
123
124 // [unspeced] If the instantiation of the module script is already attempt,
125 // early exit.
126 const auto state = module_script_->InstantiationState();
127 if (state != ModuleInstantiationState::kUninstantiated) {
128 AdvanceState(State::kFinished);
129 return;
130 }
131
132 FetchDescendants();
133
134 // Note: Step 5- continues in instantiate() method, after
135 // "fetch the descendants of a module script" procedure completes.
136 }
137
138 class ModuleTreeLinker::DependencyModuleClient
139 : public GarbageCollectedFinalized<
140 ModuleTreeLinker::DependencyModuleClient>,
141 public ModuleTreeClient {
142 USING_GARBAGE_COLLECTED_MIXIN(ModuleTreeLinker::DependencyModuleClient);
143
144 public:
145 static DependencyModuleClient* Create(ModuleTreeLinker* module_tree_linkers) {
146 return new DependencyModuleClient(module_tree_linkers);
147 }
148 virtual ~DependencyModuleClient() = default;
149
150 DEFINE_INLINE_TRACE() {
151 visitor->Trace(module_tree_linkers_);
152 ModuleTreeClient::Trace(visitor);
153 }
154
155 private:
156 DependencyModuleClient(ModuleTreeLinker* module_tree_linkers)
157 : module_tree_linkers_(module_tree_linkers) {
158 CHECK(module_tree_linkers);
159 }
160
161 // Implements ModuleTreeClient
162 void NotifyModuleTreeLoadFinished(ModuleScript*) override;
163
164 Member<ModuleTreeLinker> module_tree_linkers_;
165 };
166
167 void ModuleTreeLinker::FetchDescendants() {
168 CHECK(module_script_);
169 AdvanceState(State::kFetchingDependencies);
170
171 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendant s-of-a-module-script
172
173 // [unspeced] If the instantiation of the module script is already attempt,
174 // early exit.
175 const auto state = module_script_->InstantiationState();
176 if (state != ModuleInstantiationState::kUninstantiated) {
177 AdvanceState(State::kFinished);
178 return;
179 }
180
181 // Step 1. Let record be module script's module record.
182 ScriptModule record = module_script_->Record();
183
184 // Step 2. If record.[[RequestedModules]] is empty, asynchronously complete
185 // this algorithm with module script.
186 Vector<String> module_requests =
187 modulator_->ModuleRequestsFromScriptModule(record);
188
189 // Step 3. Let urls be a new empty list.
190 HashSet<KURL> urls;
191
192 // Step 4. For each string requested of record.[[RequestedModules]],
193 for (const auto& module_request : module_requests) {
194 // Step 4.1. Let url be the result of resolving a module specifier given
195 // module script and requested.
196 KURL url = Modulator::ResolveModuleSpecifier(module_request,
197 module_script_->BaseURL());
198 #ifndef NDEBUG
199 printf("resolveModuleSpecifier \"%s\" -> \"%s\"\n",
200 module_request.Utf8().Data(), url.GetString().Utf8().Data());
201 #endif
202
203 // Step 4.2. If the result is error: ...
204 if (url.IsNull()) {
205 // Let error be a new TypeError exception.
206 // Report the exception error for module script.
207 // TODO(kouhei): Implement exception
208 // Abort this algorithm, and asynchronously complete it with null.
209 module_script_ = nullptr;
210 AdvanceState(State::kFinished);
211 return;
212 }
213
214 // Step 4.3. Otherwise, if ancestor list does not contain url, append url to
215 // urls.
216
217 // Modulator::resolveModuleSpecifier() impl must return either a valid url
218 // or null.
219 CHECK(url.IsValid());
220 if (!ancestor_list_with_url_.Contains(url))
221 urls.insert(url);
222 }
223
224 // Step 5. For each url in urls, perform the internal module script graph
225 // fetching procedure given url, module script's credentials mode, module
226 // script's cryptographic nonce, module script's parser state, destination,
227 // module script's settings object, module script's settings object, ancestor
228 // list, module script's base URL, and with the top-level module fetch flag
229 // unset. If the caller of this algorithm specified custom perform the fetch
230 // steps, pass those along while performing the internal module script graph
231 // fetching procedure.
232 // TODO(kouhei): handle "destination".
233 CHECK_EQ(num_incomplete_descendants_, 0u);
234 if (urls.IsEmpty()) {
235 // Continue to instantiate() to process "internal module script graph
236 // fetching procedure" Step 5-.
237 Instantiate();
238 return;
239 }
240 num_incomplete_descendants_ = urls.size();
241 for (const KURL& url : urls) {
242 DependencyModuleClient* dependency_client =
243 DependencyModuleClient::Create(this);
244 dependency_clients_.insert(dependency_client);
245
246 ModuleScriptFetchRequest request(url, module_script_->Nonce(),
247 module_script_->ParserState(),
248 module_script_->CredentialsMode(),
249 module_script_->BaseURL().GetString());
250 modulator_->FetchTreeInternal(request, ancestor_list_with_url_,
251 ModuleGraphLevel::kDependentModuleFetch,
252 dependency_client);
253 }
254
255 // Asynchronously continue processing after notifyOneDescendantFinished() is
256 // called m_numIncompleteDescendants times.
257 CHECK_GT(num_incomplete_descendants_, 0u);
258 }
259
260 void ModuleTreeLinker::DependencyModuleClient::NotifyModuleTreeLoadFinished(
261 ModuleScript* module_script) {
262 DescendantLoad was_success =
263 !!module_script ? DescendantLoad::kSuccess : DescendantLoad::kFailed;
264 module_tree_linkers_->NotifyOneDescendantFinished(was_success);
265 }
266
267 void ModuleTreeLinker::NotifyOneDescendantFinished(DescendantLoad was_success) {
268 if (state_ == State::kFinished) {
269 // We may reach here if one of the descendant failed to load, and the other
270 // descendants fetches were in flight.
271 CHECK(!module_script_);
272 return;
273 }
274
275 CHECK_EQ(state_, State::kFetchingDependencies);
276
277 CHECK_GT(num_incomplete_descendants_, 0u);
278 --num_incomplete_descendants_;
279
280 if (was_success == DescendantLoad::kFailed) {
281 // One descendant failure is enough to make this module graph node fail.
282 // Ignore incomplete descendants as of now. Their success/failure
283 // would no longer affect this node.
284 // TODO(kouhei): Cancel inflight descendants fetch if possible.
285 num_incomplete_descendants_ = 0;
286
287 module_script_ = nullptr;
288 AdvanceState(State::kFinished);
289 return;
290 }
291 DCHECK_EQ(was_success, DescendantLoad::kSuccess);
292
293 CHECK(module_script_);
294 #ifndef NDEBUG
295 printf("remaining desc %zu\n", num_incomplete_descendants_);
296 #endif
297 if (!num_incomplete_descendants_)
298 Instantiate();
299 }
300
301 void ModuleTreeLinker::Instantiate() {
302 CHECK(module_script_);
303 AdvanceState(State::kInstantiating);
304
305 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
306
307 // [unspeced] If the instantiation of the module script is already attempt,
308 // early exit.
309 const auto state = module_script_->InstantiationState();
310 if (state != ModuleInstantiationState::kUninstantiated) {
311 AdvanceState(State::kFinished);
312 return;
313 }
314
315 // Step 5. Let record be result's module record.
316 ScriptModule record = module_script_->Record();
317
318 // Step 6. Let instantiationStatus be record.ModuleDeclarationInstantiation().
319 ScriptValue error = modulator_->InstantiateModule(record);
320 if (!error.IsEmpty()) {
321 module_script_->SetInstantiationError(error.GetIsolate(), error.V8Value());
322 } else {
323 module_script_->SetInstantiationSuccess();
324 }
325 ModuleInstantiationState instantiation_status =
326 module_script_->InstantiationState();
327
328 // Step 7. For each module script script in result's uninstantiated inclusive
329 // descendant module scripts, perform the following steps:
330 HeapHashSet<Member<ModuleScript>> uninstantiated_set =
331 UninstantiatedInclusiveDescendants();
332 for (const auto& descendant : uninstantiated_set) {
333 if (instantiation_status == ModuleInstantiationState::kErrored) {
334 // Step 7.1. If instantiationStatus is an abrupt completion, then set
335 // script's
336 // instantiation state to "errored", its instantiation error to
337 // instantiationStatus.[[Value]], and its module record to null.
338 DCHECK(!error.IsEmpty());
339 descendant->SetInstantiationError(error.GetIsolate(), error.V8Value());
340 descendant->ClearRecord();
341 } else {
342 // Step 7.2. Otherwise, set script's instantiation state to
343 // "instantiated".
344 descendant->SetInstantiationSuccess();
345 }
346 }
347
348 // Step 8. Asynchronously complete this algorithm with descendants result.
349 AdvanceState(State::kFinished);
350 }
351
352 HeapHashSet<Member<ModuleScript>>
353 ModuleTreeLinker::UninstantiatedInclusiveDescendants() {
354 // https://html.spec.whatwg.org/multipage/webappapis.html#uninstantiated-inclu sive-descendant-module-scripts
355 // Step 1. Let moduleMap be script's settings object's module map.
356 // Note: Modulator is our "settings object".
357 // Note: We won't reference the ModuleMap directly here to aid testing.
358
359 // Step 2. Let stack be the stack « script ».
360 // TODO(kouhei): Make stack a HeapLinkedHashSet for O(1) lookups.
361 HeapDeque<Member<ModuleScript>> stack;
362 stack.push_front(module_script_);
363
364 // Step 3. Let inclusive descendants be an empty set.
365 // Note: We use unordered set here as the order is not observable from web
366 // platform.
367 // This is allowed per spec: https://infra.spec.whatwg.org/#sets
368 HeapHashSet<Member<ModuleScript>> inclusive_descendants;
369
370 // Step 4. While stack is not empty:
371 while (!stack.IsEmpty()) {
372 // Step 4.1. Let current the result of popping from stack.
373 ModuleScript* current = stack.TakeFirst();
374
375 // Step 4.2. If inclusive descendants and stack both do not contain current,
376 // then:
377 if (inclusive_descendants.Contains(current))
378 continue;
379 if (std::find(stack.begin(), stack.end(), current) != stack.end())
380 continue;
381
382 // Step 4.2.1. Append current to inclusive descendants.
383 inclusive_descendants.insert(current);
384
385 // TODO(kouhei): This implementation is a direct transliteration of the
386 // spec. Omit intermediate vectors at the least.
387
388 // Step 4.2.2. Let child specifiers be the value of current's module
389 // record's [[RequestedModules]] internal slot.
390 Vector<String> child_specifiers =
391 modulator_->ModuleRequestsFromScriptModule(current->Record());
392 // Step 4.2.3. Let child URLs be the list obtained by calling resolve a
393 // module specifier once for each item of child specifiers, given current
394 // and that item. Omit any failures.
395 Vector<KURL> child_ur_ls;
396 for (const auto& child_specifier : child_specifiers) {
397 KURL child_url = modulator_->ResolveModuleSpecifier(child_specifier,
398 current->BaseURL());
399 if (child_url.IsValid())
400 child_ur_ls.push_back(child_url);
401 }
402 // Step 4.2.4. Let child modules be the list obtained by getting each value
403 // in moduleMap whose key is given by an item of child URLs.
404 HeapVector<Member<ModuleScript>> child_modules;
405 for (const auto& child_url : child_ur_ls) {
406 ModuleScript* module_script =
407 modulator_->GetFetchedModuleScript(child_url);
408 child_modules.push_back(module_script);
409 }
410 // Step 4.2.5. For each s in child modules that is not contained by
411 // inclusive descendants, push s onto stack.
412 for (const auto& s : child_modules) {
413 if (!inclusive_descendants.Contains(s))
414 stack.push_front(s);
415 }
416 }
417
418 // Step 5. Return a set containing all items of inclusive descendants whose
419 // instantiation state is "uninstantiated".
420 HeapHashSet<Member<ModuleScript>> uninstantiated_set;
421 for (const auto& script : inclusive_descendants) {
422 if (script->InstantiationState() ==
423 ModuleInstantiationState::kUninstantiated)
424 uninstantiated_set.insert(script);
425 }
426 return uninstantiated_set;
427 }
428
429 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698