OLD | NEW |
---|---|
(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 | |
OLD | NEW |