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

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: address haraken/yhirano comments Created 3 years, 11 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.
dominicc (has gone to gerrit) 2017/01/11 06:57:15 WDYT about making some of the DCHECKs in this clas
kouhei (in TOK) 2017/01/17 05:26:13 Done.
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/ModuleScript.h"
9 #include "core/loader/modulescript/ModuleTreeLinkerRegistry.h"
10
11 namespace blink {
12
13 ModuleTreeLinker* ModuleTreeLinker::fetch(const KURL& url,
14 const KURL& baseURL,
15 HashSet<KURL> ancestorList,
16 Modulator* modulator,
17 ModuleTreeLinkerRegistry* registry,
18 ModuleTreeClient* client) {
19 HashSet<KURL> ancestorListWithUrl = ancestorList;
20 ancestorListWithUrl.add(url);
21
22 ModuleTreeLinker* fetcher = new ModuleTreeLinker(
23 std::move(ancestorListWithUrl), modulator, registry, client);
24 fetcher->fetchSelf(url, baseURL);
25 return fetcher;
26 }
27
28 ModuleTreeLinker::ModuleTreeLinker(HashSet<KURL> ancestorListWithUrl,
29 Modulator* modulator,
30 ModuleTreeLinkerRegistry* registry,
31 ModuleTreeClient* client)
32 : m_modulator(modulator),
33 m_registry(registry),
34 m_client(client),
35 m_ancestorListWithUrl(std::move(ancestorListWithUrl)) {
36 DCHECK(modulator);
37 DCHECK(registry);
38 DCHECK(client);
39 }
40
41 DEFINE_TRACE(ModuleTreeLinker) {
42 visitor->trace(m_modulator);
43 visitor->trace(m_registry);
44 visitor->trace(m_client);
45 visitor->trace(m_moduleScript);
46 visitor->trace(m_dependencyClients);
47 SingleModuleClient::trace(visitor);
48 }
49
50 void ModuleTreeLinker::advanceState(State newState) {
51 printf("mtf %p adv state: %d\n", this, newState);
52 switch (m_state) {
53 case State::Initial:
54 DCHECK_EQ(newState, State::FetchingSelf);
55 break;
56 case State::FetchingSelf:
57 DCHECK(newState == State::FetchingDependencies ||
58 newState == State::Finished);
59 break;
60 case State::FetchingDependencies:
61 DCHECK(newState == State::Instantiating || newState == State::Finished);
62 break;
63 case State::Instantiating:
64 DCHECK_EQ(newState, State::Finished);
65 break;
66 case State::Finished:
67 NOTREACHED();
68 break;
69 default:
70 NOTREACHED();
dominicc (has gone to gerrit) 2017/01/11 06:57:15 Is there other state which can be DCHECKed at thes
kouhei (in TOK) 2017/01/17 05:26:13 Done.
71 }
72
73 m_state = newState;
74
75 if (m_state == State::Finished) {
76 m_registry->releaseFinishedFetcher(this);
77 m_client->notifyFinishedModuleTree(m_moduleScript);
78 }
79 }
80
81 void ModuleTreeLinker::fetchSelf(const KURL& url, const KURL& baseURL) {
82 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
83
84 // Step 1. Fetch a single module script given url, fetch client settings
85 // object, destination, cryptographic nonce, parser state, credentials mode,
86 // module map settings object, referrer, and the top-level module fetch flag.
87 // If the caller of this algorithm specified custom perform the fetch steps,
88 // pass those along while fetching a single module script.
89 // Step 2. Return from this algorithm, and run the following steps when
90 // fetching a single module script asynchronously completes with result.
91 advanceState(State::FetchingSelf);
92 m_modulator->fetchSingle(url, baseURL, this);
93 }
94
95 class ModuleTreeLinker::DependencyModuleClient
96 : public GarbageCollectedFinalized<
97 ModuleTreeLinker::DependencyModuleClient>,
98 public ModuleTreeClient {
99 USING_GARBAGE_COLLECTED_MIXIN(ModuleTreeLinker::DependencyModuleClient);
100
101 public:
102 static DependencyModuleClient* create(ModuleTreeLinker* moduleTreeLinkers) {
103 return new DependencyModuleClient(moduleTreeLinkers);
104 }
105 virtual ~DependencyModuleClient() = default;
106
107 DEFINE_INLINE_TRACE() {
108 visitor->trace(m_moduleTreeLinkers);
109 ModuleTreeClient::trace(visitor);
110 }
111
112 private:
113 DependencyModuleClient(ModuleTreeLinker* moduleTreeLinkers)
114 : m_moduleTreeLinkers(moduleTreeLinkers) {
115 DCHECK(moduleTreeLinkers);
116 }
117
118 // Implements ModuleTreeClient
119 void notifyFinishedModuleTree(ModuleScript*) override;
120
121 Member<ModuleTreeLinker> m_moduleTreeLinkers;
122 };
123
124 void ModuleTreeLinker::notifyFinishedSingleModule(ModuleScript* moduleScript) {
125 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
126
127 // Step 3. If result is null, asynchronously complete this algorithm with null
128 // and abort these steps.
129 if (!moduleScript) {
130 advanceState(State::Finished);
131 return;
132 }
133
134 // Step 4. Otherwise, result is a module script. Fetch the descendants of
135 // result given destination and an ancestor list obtained by appending url to
136 // ancestor list. Wait for fetching the descendants of a module script to
137 // asynchronously complete with descendants result before proceeding to the
138 // next step.
139 m_moduleScript = moduleScript;
140 fetchDescendants();
141
142 // Note: Step 5- continues in instantiate() method, after
143 // "fetch the descendants of a module script" procedure completes.
144 }
145
146 void ModuleTreeLinker::fetchDescendants() {
147 DCHECK(m_moduleScript);
148 advanceState(State::FetchingDependencies);
149
150 // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendant s-of-a-module-script
151
152 // Step 1. Let record be module script's module record.
153 ScriptModule record = m_moduleScript->record();
154
155 // Step 2. If record.[[RequestedModules]] is empty, asynchronously complete
156 // this algorithm with module script.
157 Vector<String> moduleRequests =
158 m_modulator->moduleRequestsFromScriptModule(record);
159 if (moduleRequests.isEmpty()) {
160 // Continue to instantiate() to process "internal module script graph
161 // fetching procedure" Step 5-.
162 instantiate();
163 return;
164 }
165
166 // Step 3. Let urls be a new empty list.
167 Vector<KURL> urls;
168
169 // Step 4. For each string requested of record.[[RequestedModules]],
170 for (const auto& moduleRequest : moduleRequests) {
171 // Step 4.1. Let url be the result of resolving a module specifier given
172 // module script and requested.
173 KURL url = m_modulator->resolveModuleSpecifier(moduleRequest,
174 m_moduleScript->baseURL());
175 printf("resolveModuleSpecifier \"%s\" -> \"%s\"\n",
176 moduleRequest.utf8().data(), url.getString().utf8().data());
177
178 // Step 4.2. If the result is error: ...
179 if (url.isNull()) {
180 // Let error be a new TypeError exception.
dominicc (has gone to gerrit) 2017/01/11 06:57:15 TODO might be useful here to keep track.
kouhei (in TOK) 2017/01/17 05:26:13 Done.
181 // Report the exception error for module script.
182 // Abort this algorithm, and asynchronously complete it with null.
183 m_moduleScript = nullptr;
184 advanceState(State::Finished);
185 }
186
187 // Step 4.3. Otherwise, if ancestor list does not contain url, append url to
188 // urls.
189
190 // Modulator::resolveModuleSpecifier() impl must return either a valid url
191 // or null.
192 DCHECK(url.isValid());
193 if (!m_ancestorListWithUrl.contains(url))
194 urls.push_back(url);
195 }
196
197 // Step 5. For each url in urls, perform the internal module script graph
198 // fetching procedure given url, module script's credentials mode, module
199 // script's cryptographic nonce, module script's parser state, destination,
200 // module script's settings object, module script's settings object, ancestor
201 // list, module script's base URL, and with the top-level module fetch flag
202 // unset. If the caller of this algorithm specified custom perform the fetch
203 // steps, pass those along while performing the internal module script graph
204 // fetching procedure.
205 DCHECK_EQ(m_numIncompleteDescendants, 0u);
206 m_numIncompleteDescendants = urls.size();
207 for (const KURL& url : urls) {
208 DependencyModuleClient* dependencyClient =
209 DependencyModuleClient::create(this);
210 m_dependencyClients.add(dependencyClient);
211 m_modulator->fetchTree(url, m_moduleScript->baseURL(), dependencyClient);
212 }
213
214 // Asynchronously continue processing after notifyOneDescendantFinished() is
215 // called m_numIncompleteDescendants times.
216 DCHECK(m_numIncompleteDescendants);
217 }
218
219 void ModuleTreeLinker::DependencyModuleClient::notifyFinishedModuleTree(
220 ModuleScript* moduleScript) {
221 bool wasSuccess = !!moduleScript;
222 m_moduleTreeLinkers->notifyOneDescendantFinished(wasSuccess);
223 }
224
225 void ModuleTreeLinker::notifyOneDescendantFinished(bool wasSuccess) {
226 if (m_state == State::Finished) {
227 // We may reach here if one of the descendant failed to load, and the other
228 // descendants fetches were in flight.
229 DCHECK(!m_moduleScript);
230 return;
231 }
232
233 DCHECK_EQ(m_state, State::FetchingDependencies);
234
235 DCHECK_GT(m_numIncompleteDescendants, 0u);
236 --m_numIncompleteDescendants;
237
238 if (!wasSuccess) {
239 m_moduleScript = nullptr;
240 advanceState(State::Finished);
241 return;
242 }
243
244 DCHECK(m_moduleScript);
245 printf("remaining desc %zu\n", m_numIncompleteDescendants);
246 if (!m_numIncompleteDescendants)
247 instantiate();
248 }
249
250 void ModuleTreeLinker::instantiate() {
251 DCHECK(m_moduleScript);
252 advanceState(State::Instantiating);
253
254 // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-scri pt-graph-fetching-procedure
255
256 // Step 5. Let record be result's module record.
257 ScriptModule record = m_moduleScript->record();
258
259 // Step 6. Let instantiationStatus be record.ModuleDeclarationInstantiation().
260 bool instantiateSuccess = m_modulator->instantiateModule(record);
261 DCHECK(instantiateSuccess); // TODO(kouhei)
262 m_moduleScript->updateStateAfterInstantiation(ScriptValue());
263 InstantiationState instantiationStatus = m_moduleScript->instantiationState();
264
265 // Step 7. For each module script script in result's uninstantiated inclusive
266 // descendant module scripts, perform the following steps:
267 HeapHashSet<Member<ModuleScript>> uninstantiatedSet =
268 uninstantiatedInclusiveDescendants();
269 for (const auto& descendant : uninstantiatedSet) {
270 if (instantiationStatus == InstantiationState::Errored) {
271 // Step 7.1. If instantiationStatus is an abrupt completion, then set
272 // script's
273 // instantiation state to "errored", its instantiation error to
274 // instantiationStatus.[[Value]], and its module record to null.
275 descendant->propagateUpstreamError(m_moduleScript->instantiationError());
276 } else {
277 // Step 7.2. Otherwise, set script's instantiation state to
278 // "instantiated".
279 descendant->propagateUpstreamSuccess();
280 }
281 }
282
283 // Step 8. Asynchronously complete this algorithm with descendants result.
284 advanceState(State::Finished);
285 }
286
287 HeapHashSet<Member<ModuleScript>>
288 ModuleTreeLinker::uninstantiatedInclusiveDescendants() {
289 // https://html.spec.whatwg.org/multipage/webappapis.html#uninstantiated-inclu sive-descendant-module-scripts
290 // Step 1. Let moduleMap be script's settings object's module map.
291 // Note: Modulator is our "settings object".
292 // Note: We won't reference the ModuleMap directly here to aid testing.
293
294 // Step 2. Let stack be the stack « script ».
295 HeapDeque<Member<ModuleScript>> stack;
296 stack.prepend(m_moduleScript);
297
298 // Step 3. Let inclusive descendants be an empty set.
299 // Note: We use unordered set here as the order is not observable from web
300 // platform.
301 // This is allowed per spec: https://infra.spec.whatwg.org/#sets
302 HeapHashSet<Member<ModuleScript>> inclusiveDescendants;
303
304 // Step 4. While stack is not empty:
305 while (!stack.isEmpty()) {
306 // Step 4.1. Let current the result of popping from stack.
307 ModuleScript* current = stack.takeFirst();
308
309 // Step 4.2. If inclusive descendants and stack both do not contain current,
310 // then:
311 if (inclusiveDescendants.contains(current))
312 continue;
313 if (std::find(stack.begin(), stack.end(), current) != stack.end())
dominicc (has gone to gerrit) 2017/01/11 06:57:15 How deep does this get? Would it make sense to hav
kouhei (in TOK) 2017/01/17 05:26:13 Added a TODO. uninstantiatedInclusiveDescendants()
314 continue;
315
316 // Step 4.2.1. Append current to inclusive descendants.
317 inclusiveDescendants.add(current);
318
319 // TODO(kouhei): This implementation is a direct transliteration of the
320 // spec. Omit intermediate vectors at the least.
321
322 // Step 4.2.2. Let child specifiers be the value of current's module
323 // record's [[RequestedModules]] internal slot.
324 Vector<String> childSpecifiers =
325 m_modulator->moduleRequestsFromScriptModule(current->record());
326 // Step 4.2.3. Let child URLs be the list obtained by calling resolve a
327 // module specifier once for each item of child specifiers, given current
328 // and that item. Omit any failures.
329 Vector<KURL> childURLs;
330 for (const auto& childSpecifier : childSpecifiers) {
331 KURL childURL = m_modulator->resolveModuleSpecifier(childSpecifier,
332 current->baseURL());
333 if (childURL.isValid())
334 childURLs.push_back(childURL);
335 }
336 // Step 4.2.4. Let child modules be the list obtained by getting each value
337 // in moduleMap whose key is given by an item of child URLs.
338 HeapVector<Member<ModuleScript>> childModules;
339 for (const auto& childURL : childURLs) {
340 ModuleScript* moduleScript =
341 m_modulator->retrieveFetchedModuleScript(childURL);
342 childModules.push_back(moduleScript);
343 }
344 // Step 4.2.5. For each s in child modules that is not contained by
345 // inclusive descendants, push s onto stack.
346 for (const auto& s : childModules) {
347 if (!inclusiveDescendants.contains(s))
348 stack.prepend(s);
349 }
350 }
351
352 // Step 5. Return a set containing all items of inclusive descendants whose
353 // instantiation state is "uninstantiated".
354 HeapHashSet<Member<ModuleScript>> uninstantiatedSet;
355 for (const auto& script : inclusiveDescendants) {
356 if (script->instantiationState() == InstantiationState::Uninstantiated)
357 uninstantiatedSet.add(script);
358 }
359 return uninstantiatedSet;
360 }
361
362 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698