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

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

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

Powered by Google App Engine
This is Rietveld 408576698