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

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

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

Powered by Google App Engine
This is Rietveld 408576698