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

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

Issue 2823803003: [ES6 modules] Introduce ModuleTreeLinker (Closed)
Patch Set: yhirano2 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 "platform/loader/fetch/ResourceLoadingLog.h"
13 #include "platform/wtf/HashSet.h"
14
15 namespace blink {
16
17 ModuleTreeLinker* ModuleTreeLinker::Fetch(
18 const ModuleScriptFetchRequest& request,
19 const AncestorList& ancestor_list,
20 ModuleGraphLevel level,
21 Modulator* modulator,
22 ModuleTreeLinkerRegistry* registry,
23 ModuleTreeClient* client) {
24 AncestorList ancestor_list_with_url = ancestor_list;
25 ancestor_list_with_url.insert(request.Url());
26
27 ModuleTreeLinker* fetcher =
28 new ModuleTreeLinker(ancestor_list_with_url, modulator, registry, client);
29 fetcher->FetchSelf(request, level);
30 return fetcher;
31 }
32
33 ModuleTreeLinker::ModuleTreeLinker(const AncestorList& ancestor_list_with_url,
34 Modulator* modulator,
35 ModuleTreeLinkerRegistry* registry,
36 ModuleTreeClient* client)
37 : modulator_(modulator),
38 registry_(registry),
39 client_(client),
40 ancestor_list_with_url_(ancestor_list_with_url) {
41 CHECK(modulator);
42 CHECK(registry);
43 CHECK(client);
44 }
45
46 DEFINE_TRACE(ModuleTreeLinker) {
47 visitor->Trace(modulator_);
48 visitor->Trace(registry_);
49 visitor->Trace(client_);
50 visitor->Trace(module_script_);
51 visitor->Trace(descendants_module_script_);
52 visitor->Trace(dependency_clients_);
53 SingleModuleClient::Trace(visitor);
54 }
55
56 #if DCHECK_IS_ON()
57 const char* ModuleTreeLinker::StateToString(ModuleTreeLinker::State state) {
58 switch (state) {
59 case State::kInitial:
60 return "Initial";
61 case State::kFetchingSelf:
62 return "FetchingSelf";
63 case State::kFetchingDependencies:
64 return "FetchingDependencies";
65 case State::kInstantiating:
66 return "Instantiating";
67 case State::kFinished:
68 return "Finished";
69 }
70 NOTREACHED();
71 return "";
72 }
73 #endif
74
75 void ModuleTreeLinker::AdvanceState(State new_state) {
76 #if DCHECK_IS_ON()
77 RESOURCE_LOADING_DVLOG(1)
78 << "ModuleTreeLinker[" << this << "]::advanceState("
79 << StateToString(state_) << " -> " << StateToString(new_state) << ")";
80 #endif
81
82 switch (state_) {
83 case State::kInitial:
84 CHECK_EQ(num_incomplete_descendants_, 0u);
85 CHECK_EQ(new_state, State::kFetchingSelf);
86 break;
87 case State::kFetchingSelf:
88 CHECK_EQ(num_incomplete_descendants_, 0u);
89 CHECK(new_state == State::kFetchingDependencies ||
90 (!descendants_module_script_ && new_state == State::kFinished));
91 break;
92 case State::kFetchingDependencies:
93 CHECK_EQ(new_state, State::kInstantiating);
94 DCHECK(!num_incomplete_descendants_ || !descendants_module_script_)
95 << num_incomplete_descendants_
96 << " outstanding descendant loads found, but the descendant module "
97 "script load procedure unexpectedly finished with "
98 << (!!descendants_module_script_ ? "success." : "failure.");
hiroshige 2017/04/19 22:24:08 Here |descendants_module_script_| is always true w
kouhei (in TOK) 2017/04/24 01:06:21 num_incomplete_descendants_ may be 0?
hiroshige 2017/04/24 17:56:51 Er, I meant |(!!descendants_module_script_ ? "succ
99 break;
100 case State::kInstantiating:
101 CHECK(num_incomplete_descendants_ == 0u || !descendants_module_script_);
102 CHECK_EQ(new_state, State::kFinished);
103 break;
104 case State::kFinished:
105 NOTREACHED();
hiroshige 2017/04/19 22:24:08 As we test state transition by CHECK()s in other c
kouhei (in TOK) 2017/04/24 01:06:22 I'd leave this for now.
106 break;
107 }
108
109 state_ = new_state;
110
111 if (state_ == State::kFinished) {
112 registry_->ReleaseFinishedFetcher(this);
113
114 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-sc ript-graph-fetching-procedure
115 // Step 8. Asynchronously complete this algorithm with descendants result.
116 client_->NotifyModuleTreeLoadFinished(descendants_module_script_);
117 }
118 }
119
120 void ModuleTreeLinker::FetchSelf(const ModuleScriptFetchRequest& request,
121 ModuleGraphLevel level) {
122 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
123
124 // Step 1. Fetch a single module script given url, fetch client settings
125 // object, destination, cryptographic nonce, parser state, credentials mode,
126 // module map settings object, referrer, and the top-level module fetch flag.
127 // If the caller of this algorithm specified custom perform the fetch steps,
128 // pass those along while fetching a single module script.
129 AdvanceState(State::kFetchingSelf);
130 modulator_->FetchSingle(request, level, this);
131
132 // Step 2. Return from this algorithm, and run the following steps when
133 // fetching a single module script asynchronously completes with result.
134 // Note: Modulator::fetchSingle asynchronously notifies result to
hiroshige 2017/04/19 22:24:07 nit: FetchSingle().
kouhei (in TOK) 2017/04/24 01:06:21 Done.
135 // ModuleTreeLinker::notifyModuleLoadFinished().
136 }
137
138 void ModuleTreeLinker::NotifyModuleLoadFinished(ModuleScript* module_script) {
139 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
140
141 // Step 3. "If result is null, ..."
142 if (!module_script) {
143 // "asynchronously complete this algorithm with null and abort these steps."
144 // Note: The return variable for "internal module script graph fetching
145 // procedure" is descendants_module_script_ per Step 8.
hiroshige 2017/04/19 22:24:07 Lines 144-145 should refer to our implementation (
kouhei (in TOK) 2017/04/24 01:06:22 Done.
146 DCHECK(!descendants_module_script_);
147 AdvanceState(State::kFinished);
148 return;
149 }
150
151 // Step 4. Otherwise, result is a module script. Fetch the descendants of
152 // result given destination and an ancestor list obtained by appending url to
153 // ancestor list. Wait for fetching the descendants of a module script to
154 // asynchronously complete with descendants result before proceeding to the
155 // next step.
156 module_script_ = module_script;
157
158 FetchDescendants();
159
160 // Note: Step 5- continues in instantiate() method, after
kinuko 2017/04/19 06:11:33 nit: instantiate() -> Instantiate()
kouhei (in TOK) 2017/04/24 01:06:22 Done.
161 // "fetch the descendants of a module script" procedure completes.
162 }
163
164 class ModuleTreeLinker::DependencyModuleClient
165 : public GarbageCollectedFinalized<
166 ModuleTreeLinker::DependencyModuleClient>,
167 public ModuleTreeClient {
168 USING_GARBAGE_COLLECTED_MIXIN(ModuleTreeLinker::DependencyModuleClient);
169
170 public:
171 static DependencyModuleClient* Create(ModuleTreeLinker* module_tree_linker) {
172 return new DependencyModuleClient(module_tree_linker);
173 }
174 virtual ~DependencyModuleClient() = default;
175
176 DEFINE_INLINE_TRACE() {
177 visitor->Trace(module_tree_linker_);
178 ModuleTreeClient::Trace(visitor);
179 }
180
181 private:
182 DependencyModuleClient(ModuleTreeLinker* module_tree_linker)
kinuko 2017/04/19 06:11:33 nit: explicit
kouhei (in TOK) 2017/04/24 01:06:22 Done.
183 : module_tree_linker_(module_tree_linker) {
184 CHECK(module_tree_linker);
185 }
186
187 // Implements ModuleTreeClient
188 void NotifyModuleTreeLoadFinished(ModuleScript*) override;
189
190 Member<ModuleTreeLinker> module_tree_linker_;
191 };
192
193 void ModuleTreeLinker::FetchDescendants() {
194 CHECK(module_script_);
195 AdvanceState(State::kFetchingDependencies);
196
197 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendant s-of-a-module-script
198
199 // Step 1. Let record be module script's module record.
200 ScriptModule record = module_script_->Record();
201
202 // Step 2. If record.[[RequestedModules]] is empty, asynchronously complete
203 // this algorithm with module script.
204 Vector<String> module_requests =
205 modulator_->ModuleRequestsFromScriptModule(record);
206
207 // Step 3. Let urls be a new empty list.
hiroshige 2017/04/19 22:24:07 The spec says "list" while the implementation is "
kouhei (in TOK) 2017/04/24 01:06:22 We don't actually require HashSet<> here, so chang
208 HashSet<KURL> urls;
209
210 // Step 4. For each string requested of record.[[RequestedModules]],
211 for (const auto& module_request : module_requests) {
212 // Step 4.1. Let url be the result of resolving a module specifier given
213 // module script and requested.
214 KURL url = Modulator::ResolveModuleSpecifier(module_request,
215 module_script_->BaseURL());
216
217 // Step 4.2. If the result is error: ...
218 if (url.IsNull()) {
219 // Let error be a new TypeError exception.
220 // Report the exception error for module script.
221 // TODO(kouhei): Implement the exception.
222
223 // Abort this algorithm, and asynchronously complete it with null.
224 // Note: The return variable for "internal module script graph fetching
225 // procedure" is descendants_module_script_ per Step 8.
226 DCHECK(!descendants_module_script_);
227 // Note: while we complete "fetch the descendants of a module script"
228 // algorithm here, we still need to continue to the rest of the
229 // steps in "internal module script graph fetching procedure"
230 Instantiate();
231 return;
232 }
233
234 // Step 4.3. Otherwise, if ancestor list does not contain url, append url to
235 // urls.
236
237 // Modulator::resolveModuleSpecifier() impl must return either a valid url
238 // or null.
239 CHECK(url.IsValid());
240 if (!ancestor_list_with_url_.Contains(url))
241 urls.insert(url);
242 }
243
244 // Step 5. For each url in urls, perform the internal module script graph
245 // fetching procedure given url, module script's credentials mode, module
246 // script's cryptographic nonce, module script's parser state, destination,
247 // module script's settings object, module script's settings object, ancestor
248 // list, module script's base URL, and with the top-level module fetch flag
249 // unset. If the caller of this algorithm specified custom perform the fetch
250 // steps, pass those along while performing the internal module script graph
251 // fetching procedure.
252 // TODO(kouhei): handle "destination".
253 CHECK_EQ(num_incomplete_descendants_, 0u);
254 if (urls.IsEmpty()) {
hiroshige 2017/04/19 22:24:07 Actually, this if block implements Step 2 above. (
kouhei (in TOK) 2017/04/24 01:06:22 Done.
hiroshige 2017/04/26 01:23:58 Sorry I was wrong: |urls| can be empty here if the
255 // Continue to instantiate() to process "internal module script graph
kinuko 2017/04/19 06:11:33 nit: instantiate() -> Instantiate()
kouhei (in TOK) 2017/04/24 01:06:22 Done.
256 // fetching procedure" Step 5-.
257 descendants_module_script_ = module_script_;
258 Instantiate();
259 return;
260 }
261 num_incomplete_descendants_ = urls.size();
262 for (const KURL& url : urls) {
263 DependencyModuleClient* dependency_client =
264 DependencyModuleClient::Create(this);
265 dependency_clients_.insert(dependency_client);
266
267 ModuleScriptFetchRequest request(url, module_script_->Nonce(),
268 module_script_->ParserState(),
269 module_script_->CredentialsMode(),
270 module_script_->BaseURL().GetString());
271 modulator_->FetchTreeInternal(request, ancestor_list_with_url_,
272 ModuleGraphLevel::kDependentModuleFetch,
273 dependency_client);
274 }
275
276 // Asynchronously continue processing after notifyOneDescendantFinished() is
277 // called m_numIncompleteDescendants times.
278 CHECK_GT(num_incomplete_descendants_, 0u);
279 }
280
281 void ModuleTreeLinker::DependencyModuleClient::NotifyModuleTreeLoadFinished(
282 ModuleScript* module_script) {
283 DescendantLoad was_success =
284 !!module_script ? DescendantLoad::kSuccess : DescendantLoad::kFailed;
hiroshige 2017/04/19 22:24:07 nit: do we need |!!|?
kouhei (in TOK) 2017/04/24 01:06:22 Removed. This is the second time I got the comment
285 module_tree_linker_->NotifyOneDescendantFinished(was_success);
286 }
287
288 void ModuleTreeLinker::NotifyOneDescendantFinished(DescendantLoad was_success) {
289 CHECK(!descendants_module_script_);
290
291 if (state_ == State::kFinished) {
292 // We may reach here if one of the descendant failed to load, and the other
293 // descendants fetches were in flight.
294 return;
295 }
296 CHECK_EQ(state_, State::kFetchingDependencies);
297
298 CHECK_GT(num_incomplete_descendants_, 0u);
299 --num_incomplete_descendants_;
300
301 // TODO(kouhei): Potential room for optimization. Cancel inflight descendants
302 // fetch if a descendant load failed.
303
304 CHECK(module_script_);
305 RESOURCE_LOADING_DVLOG(1)
306 << "ModuleTreeLinker[" << this << "]::NotifyOneDescendantFinished with "
307 << (was_success == DescendantLoad::kSuccess ? "success. " : "failure. ")
308 << num_incomplete_descendants_ << " remaining descendants.";
309
310 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendant s-of-a-module-script
311 // Step 5. "... If any of them asynchronously complete with null, then
312 // asynchronously complete this algorithm with null"
313 if (was_success == DescendantLoad::kFailed) {
314 DCHECK(!descendants_module_script_);
315 // Note: while we complete "fetch the descendants of a module script"
316 // algorithm here, we still need to continue to the rest of the steps
317 // in "internal module script graph fetching procedure"
318 Instantiate();
319 return;
320 }
321
322 // Step 5. "Wait for all of the internal module script graph fetching
323 // procedure invocations to asynchronously complete..."
hiroshige 2017/04/19 22:24:07 Add "Otherwise, asynchronously complete this algor
kouhei (in TOK) 2017/04/24 01:06:22 Done.
324 if (!num_incomplete_descendants_) {
325 descendants_module_script_ = module_script_;
326 Instantiate();
327 return;
kinuko 2017/04/19 06:11:33 Not needed?
kouhei (in TOK) 2017/04/24 01:06:22 Done.
328 }
329 }
330
331 void ModuleTreeLinker::Instantiate() {
332 CHECK(module_script_);
333 AdvanceState(State::kInstantiating);
334
335 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
336
337 // Step 5. Let record be result's module record.
338 ScriptModule record = module_script_->Record();
339
340 // Step 6. Let instantiationStatus be record.ModuleDeclarationInstantiation().
341 // Note: The |error| variable corresponds to spec variable
342 // "instantiationStatus". If |error| is empty, it indicates successful
343 // completion.
344 ScriptValue error = modulator_->InstantiateModule(record);
345
346 // Step 7. For each module script script in result's uninstantiated inclusive
347 // descendant module scripts, perform the following steps:
348 HeapHashSet<Member<ModuleScript>> uninstantiated_set =
349 UninstantiatedInclusiveDescendants();
350 for (const auto& descendant : uninstantiated_set) {
351 if (!error.IsEmpty()) {
352 // Step 7.1. If instantiationStatus is an abrupt completion, then set
353 // script's instantiation state to "errored", its instantiation error to
354 // instantiationStatus.[[Value]], and its module record to null.
355 descendant->SetInstantiationErrorAndClearRecord(error);
356 } else {
357 // Step 7.2. Otherwise, set script's instantiation state to
358 // "instantiated".
359 descendant->SetInstantiationSuccess();
360 }
361 }
362
363 // Step 8. Asynchronously complete this algorithm with descendants result.
364 AdvanceState(State::kFinished);
365 }
366
367 HeapHashSet<Member<ModuleScript>>
368 ModuleTreeLinker::UninstantiatedInclusiveDescendants() {
369 // https://html.spec.whatwg.org/multipage/webappapis.html#uninstantiated-inclu sive-descendant-module-scripts
370 // Step 1. Let moduleMap be script's settings object's module map.
371 // Note: Modulator is our "settings object".
372 // Note: We won't reference the ModuleMap directly here to aid testing.
373
374 // Step 2. Let stack be the stack « script ».
hiroshige 2017/04/19 22:24:07 nit: I prefer avoiding non-ASCII characters in the
kouhei (in TOK) 2017/04/24 01:06:22 Done.
375 // TODO(kouhei): Make stack a HeapLinkedHashSet for O(1) lookups.
376 HeapDeque<Member<ModuleScript>> stack;
377 stack.push_front(module_script_);
378
379 // Step 3. Let inclusive descendants be an empty set.
380 // Note: We use unordered set here as the order is not observable from web
381 // platform.
382 // This is allowed per spec: https://infra.spec.whatwg.org/#sets
383 HeapHashSet<Member<ModuleScript>> inclusive_descendants;
384
385 // Step 4. While stack is not empty:
386 while (!stack.IsEmpty()) {
387 // Step 4.1. Let current the result of popping from stack.
388 ModuleScript* current = stack.TakeFirst();
389
390 // Step 4.2. Assert: current is a module script (i.e., it is not "fetching"
391 // or null).
392 DCHECK(current);
393
394 // Step 4.3. If inclusive descendants and stack both do not contain current,
395 // then:
396 if (inclusive_descendants.Contains(current))
397 continue;
398 if (std::find(stack.begin(), stack.end(), current) != stack.end())
399 continue;
400
401 // Step 4.3.1. Append current to inclusive descendants.
402 inclusive_descendants.insert(current);
403
404 // TODO(kouhei): This implementation is a direct transliteration of the
405 // spec. Omit intermediate vectors at the least.
406
407 // Step 4.3.2. Let child specifiers be the value of current's module
408 // record's [[RequestedModules]] internal slot.
409 Vector<String> child_specifiers =
410 modulator_->ModuleRequestsFromScriptModule(current->Record());
411 // Step 4.3.3. Let child URLs be the list obtained by calling resolve a
412 // module specifier once for each item of child specifiers, given current
413 // and that item. Omit any failures.
414 Vector<KURL> child_urls;
415 for (const auto& child_specifier : child_specifiers) {
416 KURL child_url = modulator_->ResolveModuleSpecifier(child_specifier,
417 current->BaseURL());
418 if (child_url.IsValid())
419 child_urls.push_back(child_url);
420 }
421 // Step 4.3.4. Let child modules be the list obtained by getting each value
422 // in moduleMap whose key is given by an item of child URLs.
423 HeapVector<Member<ModuleScript>> child_modules;
424 for (const auto& child_url : child_urls) {
425 ModuleScript* module_script =
426 modulator_->GetFetchedModuleScript(child_url);
427
428 child_modules.push_back(module_script);
429 }
430 // Step 4.3.5. For each s of child modules:
431 for (const auto& s : child_modules) {
432 // Step 4.3.5.2. If s is null, continue.
433 // Note: We do null check first, as Blink HashSet can't contain nullptr.
434 // Step 4.3.5.3. Assert: s is a module script (i.e., it is not "fetching",
435 // since by this point all child modules must have been fetched). Note:
hiroshige 2017/04/19 22:24:07 nit: line break before "Note:".
kouhei (in TOK) 2017/04/24 01:06:22 Done.
436 // GetFetchedModuleScript returns nullptr if "fetching"
437 if (!s)
438 continue;
439
440 // Step 4.3.5.1. If inclusive descendants already contains s, continue.
441 if (inclusive_descendants.Contains(s))
442 continue;
443
444 // Step 4.3.5.4. Push s onto satck.
kinuko 2017/04/19 06:11:33 satck -> stack
kouhei (in TOK) 2017/04/24 01:06:22 Done.
445 stack.push_front(s);
446 }
447 }
448
449 // Step 5. Return a set containing all items of inclusive descendants whose
450 // instantiation state is "uninstantiated".
451 HeapHashSet<Member<ModuleScript>> uninstantiated_set;
452 for (const auto& script : inclusive_descendants) {
453 if (script->InstantiationState() ==
454 ModuleInstantiationState::kUninstantiated)
455 uninstantiated_set.insert(script);
456 }
457 return uninstantiated_set;
458 }
459
460 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698