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

Unified 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, 9 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
diff --git a/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c200a181d489a0d62f9b0d71be97787800a65fe6
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/modulescript/ModuleTreeLinker.cpp
@@ -0,0 +1,428 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/loader/modulescript/ModuleTreeLinker.h"
+
+#include "bindings/core/v8/ScriptModule.h"
+#include "core/dom/AncestorList.h"
+#include "core/dom/ModuleScript.h"
+#include "core/loader/modulescript/ModuleScriptFetchRequest.h"
+#include "core/loader/modulescript/ModuleTreeLinkerRegistry.h"
+#include "wtf/HashSet.h"
+
+namespace blink {
+
+ModuleTreeLinker* ModuleTreeLinker::fetch(
+ const ModuleScriptFetchRequest& request,
+ const AncestorList& ancestorList,
+ ModuleGraphLevel level,
+ Modulator* modulator,
+ ModuleTreeLinkerRegistry* registry,
+ ModuleTreeClient* client) {
+ AncestorList ancestorListWithUrl = ancestorList;
+ ancestorListWithUrl.insert(request.url());
+
+ ModuleTreeLinker* fetcher =
+ new ModuleTreeLinker(ancestorListWithUrl, modulator, registry, client);
+ fetcher->fetchSelf(request, level);
+ return fetcher;
+}
+
+ModuleTreeLinker::ModuleTreeLinker(const AncestorList& ancestorListWithUrl,
+ Modulator* modulator,
+ ModuleTreeLinkerRegistry* registry,
+ ModuleTreeClient* client)
+ : m_modulator(modulator),
+ m_registry(registry),
+ m_client(client),
+ m_ancestorListWithUrl(ancestorListWithUrl) {
+ CHECK(modulator);
+ CHECK(registry);
+ CHECK(client);
+}
+
+DEFINE_TRACE(ModuleTreeLinker) {
+ visitor->trace(m_modulator);
+ visitor->trace(m_registry);
+ visitor->trace(m_client);
+ visitor->trace(m_moduleScript);
+ visitor->trace(m_dependencyClients);
+ SingleModuleClient::trace(visitor);
+}
+
+void ModuleTreeLinker::advanceState(State newState) {
+#ifndef NDEBUG
+ printf("mtf %p adv state: %d\n", this, newState);
+#endif
+ CHECK_EQ(m_numIncompleteDescendants, 0u);
+ switch (m_state) {
+ case State::Initial:
+ CHECK_EQ(newState, State::FetchingSelf);
+ break;
+ case State::FetchingSelf:
+ CHECK(newState == State::FetchingDependencies ||
+ newState == State::Finished);
+ break;
+ case State::FetchingDependencies:
+ CHECK(newState == State::Instantiating || newState == State::Finished);
+ break;
+ case State::Instantiating:
+ CHECK_EQ(newState, State::Finished);
+ break;
+ case State::Finished:
+ NOTREACHED();
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ m_state = newState;
+
+ if (m_state == State::Finished) {
+ m_registry->releaseFinishedFetcher(this);
+ m_client->notifyModuleTreeLoadFinished(m_moduleScript);
+ }
+}
+
+void ModuleTreeLinker::fetchSelf(const ModuleScriptFetchRequest& request,
+ ModuleGraphLevel level) {
+ // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
+
+ // Step 1. Fetch a single module script given url, fetch client settings
+ // object, destination, cryptographic nonce, parser state, credentials mode,
+ // module map settings object, referrer, and the top-level module fetch flag.
+ // If the caller of this algorithm specified custom perform the fetch steps,
+ // pass those along while fetching a single module script.
+ advanceState(State::FetchingSelf);
+ m_modulator->fetchSingle(request, level, this);
+
+ // Step 2. Return from this algorithm, and run the following steps when
+ // fetching a single module script asynchronously completes with result.
+ // Note: Modulator::fetchSingle asynchronously notifies result to
+ // ModuleTreeLinker::notifyModuleLoadFinished().
+}
+
+void ModuleTreeLinker::notifyModuleLoadFinished(ModuleScript* moduleScript) {
+ // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
+
+ // Step 3. If result is null, asynchronously complete this algorithm with null
+ // and abort these steps.
+ if (!moduleScript) {
+ advanceState(State::Finished);
+ return;
+ }
+
+ // Step 4. Otherwise, result is a module script. Fetch the descendants of
+ // result given destination and an ancestor list obtained by appending url to
+ // ancestor list. Wait for fetching the descendants of a module script to
+ // asynchronously complete with descendants result before proceeding to the
+ // next step.
+ m_moduleScript = moduleScript;
+
+ // [unspeced] If the instantiation of the module script is already attempt,
+ // early exit.
+ const auto state = m_moduleScript->instantiationState();
+ if (state != ModuleInstantiationState::Uninstantiated) {
+ advanceState(State::Finished);
+ return;
+ }
+
+ fetchDescendants();
+
+ // Note: Step 5- continues in instantiate() method, after
+ // "fetch the descendants of a module script" procedure completes.
+}
+
+class ModuleTreeLinker::DependencyModuleClient
+ : public GarbageCollectedFinalized<
+ ModuleTreeLinker::DependencyModuleClient>,
+ public ModuleTreeClient {
+ USING_GARBAGE_COLLECTED_MIXIN(ModuleTreeLinker::DependencyModuleClient);
+
+ public:
+ static DependencyModuleClient* create(ModuleTreeLinker* moduleTreeLinkers) {
+ return new DependencyModuleClient(moduleTreeLinkers);
+ }
+ virtual ~DependencyModuleClient() = default;
+
+ DEFINE_INLINE_TRACE() {
+ visitor->trace(m_moduleTreeLinkers);
+ ModuleTreeClient::trace(visitor);
+ }
+
+ private:
+ DependencyModuleClient(ModuleTreeLinker* moduleTreeLinkers)
+ : m_moduleTreeLinkers(moduleTreeLinkers) {
+ CHECK(moduleTreeLinkers);
+ }
+
+ // Implements ModuleTreeClient
+ void notifyModuleTreeLoadFinished(ModuleScript*) override;
+
+ Member<ModuleTreeLinker> m_moduleTreeLinkers;
+};
+
+void ModuleTreeLinker::fetchDescendants() {
+ CHECK(m_moduleScript);
+ advanceState(State::FetchingDependencies);
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-the-descendants-of-a-module-script
+
+ // [unspeced] If the instantiation of the module script is already attempt,
+ // early exit.
+ const auto state = m_moduleScript->instantiationState();
+ if (state != ModuleInstantiationState::Uninstantiated) {
+ advanceState(State::Finished);
+ return;
+ }
+
+ // Step 1. Let record be module script's module record.
+ ScriptModule record = m_moduleScript->record();
+
+ // Step 2. If record.[[RequestedModules]] is empty, asynchronously complete
+ // this algorithm with module script.
+ Vector<String> moduleRequests =
+ m_modulator->moduleRequestsFromScriptModule(record);
+
+ // Step 3. Let urls be a new empty list.
+ HashSet<KURL> urls;
+
+ // Step 4. For each string requested of record.[[RequestedModules]],
+ for (const auto& moduleRequest : moduleRequests) {
+ // Step 4.1. Let url be the result of resolving a module specifier given
+ // module script and requested.
+ KURL url = Modulator::resolveModuleSpecifier(moduleRequest,
+ m_moduleScript->baseURL());
+#ifndef NDEBUG
+ printf("resolveModuleSpecifier \"%s\" -> \"%s\"\n",
+ moduleRequest.utf8().data(), url.getString().utf8().data());
+#endif
+
+ // Step 4.2. If the result is error: ...
+ if (url.isNull()) {
+ // Let error be a new TypeError exception.
+ // Report the exception error for module script.
+ // TODO(kouhei): Implement exception
+ // Abort this algorithm, and asynchronously complete it with null.
+ m_moduleScript = nullptr;
+ advanceState(State::Finished);
+ return;
+ }
+
+ // Step 4.3. Otherwise, if ancestor list does not contain url, append url to
+ // urls.
+
+ // Modulator::resolveModuleSpecifier() impl must return either a valid url
+ // or null.
+ CHECK(url.isValid());
+ if (!m_ancestorListWithUrl.contains(url))
+ urls.insert(url);
+ }
+
+ // Step 5. For each url in urls, perform the internal module script graph
+ // fetching procedure given url, module script's credentials mode, module
+ // script's cryptographic nonce, module script's parser state, destination,
+ // module script's settings object, module script's settings object, ancestor
+ // list, module script's base URL, and with the top-level module fetch flag
+ // unset. If the caller of this algorithm specified custom perform the fetch
+ // steps, pass those along while performing the internal module script graph
+ // fetching procedure.
+ // TODO(kouhei): handle "destination".
+ CHECK_EQ(m_numIncompleteDescendants, 0u);
+ if (urls.isEmpty()) {
+ // Continue to instantiate() to process "internal module script graph
+ // fetching procedure" Step 5-.
+ instantiate();
+ return;
+ }
+ m_numIncompleteDescendants = urls.size();
+ for (const KURL& url : urls) {
+ DependencyModuleClient* dependencyClient =
+ DependencyModuleClient::create(this);
+ m_dependencyClients.insert(dependencyClient);
+
+ ModuleScriptFetchRequest request(url, m_moduleScript->nonce(),
+ m_moduleScript->parserState(),
+ m_moduleScript->credentialsMode(),
+ m_moduleScript->baseURL().getString());
+ m_modulator->fetchTreeInternal(request, m_ancestorListWithUrl,
+ ModuleGraphLevel::DependentModuleFetch,
+ dependencyClient);
+ }
+
+ // Asynchronously continue processing after notifyOneDescendantFinished() is
+ // called m_numIncompleteDescendants times.
+ CHECK_GT(m_numIncompleteDescendants, 0u);
+}
+
+void ModuleTreeLinker::DependencyModuleClient::notifyModuleTreeLoadFinished(
+ ModuleScript* moduleScript) {
+ DescendantLoad wasSuccess =
+ !!moduleScript ? DescendantLoad::Success : DescendantLoad::Failed;
+ m_moduleTreeLinkers->notifyOneDescendantFinished(wasSuccess);
+}
+
+void ModuleTreeLinker::notifyOneDescendantFinished(DescendantLoad wasSuccess) {
+ if (m_state == State::Finished) {
+ // We may reach here if one of the descendant failed to load, and the other
+ // descendants fetches were in flight.
+ CHECK(!m_moduleScript);
+ return;
+ }
+
+ CHECK_EQ(m_state, State::FetchingDependencies);
+
+ CHECK_GT(m_numIncompleteDescendants, 0u);
+ --m_numIncompleteDescendants;
+
+ if (wasSuccess == DescendantLoad::Failed) {
+ // One descendant failure is enough to make this module graph node fail.
+ // Ignore incomplete descendants as of now. Their success/failure
+ // would no longer affect this node.
+ // TODO(kouhei): Cancel inflight descendants fetch if possible.
+ m_numIncompleteDescendants = 0;
+
+ m_moduleScript = nullptr;
+ advanceState(State::Finished);
+ return;
+ }
+ DCHECK_EQ(wasSuccess, DescendantLoad::Success);
+
+ CHECK(m_moduleScript);
+#ifndef NDEBUG
+ printf("remaining desc %zu\n", m_numIncompleteDescendants);
+#endif
+ if (!m_numIncompleteDescendants)
+ instantiate();
+}
+
+void ModuleTreeLinker::instantiate() {
+ CHECK(m_moduleScript);
+ advanceState(State::Instantiating);
+
+ // https://html.spec.whatwg.org/multipage/webappapis.html#internal-module-script-graph-fetching-procedure
+
+ // [unspeced] If the instantiation of the module script is already attempt,
+ // early exit.
+ const auto state = m_moduleScript->instantiationState();
+ if (state != ModuleInstantiationState::Uninstantiated) {
+ advanceState(State::Finished);
+ return;
+ }
+
+ // Step 5. Let record be result's module record.
+ ScriptModule record = m_moduleScript->record();
+
+ // Step 6. Let instantiationStatus be record.ModuleDeclarationInstantiation().
+ ScriptValue error = m_modulator->instantiateModule(record);
+ if (!error.isEmpty()) {
hiroshige 2017/03/31 06:21:47 According to the spec, Lines 319--325 is not neede
+ m_moduleScript->setInstantiationError(error.isolate(), error.v8Value());
+ } else {
+ m_moduleScript->setInstantiationSuccess();
+ }
+ ModuleInstantiationState instantiationStatus =
+ m_moduleScript->instantiationState();
+
+ // Step 7. For each module script script in result's uninstantiated inclusive
+ // descendant module scripts, perform the following steps:
+ HeapHashSet<Member<ModuleScript>> uninstantiatedSet =
+ uninstantiatedInclusiveDescendants();
+ for (const auto& descendant : uninstantiatedSet) {
+ if (instantiationStatus == ModuleInstantiationState::Errored) {
+ // Step 7.1. If instantiationStatus is an abrupt completion, then set
+ // script's
+ // instantiation state to "errored", its instantiation error to
+ // instantiationStatus.[[Value]], and its module record to null.
+ DCHECK(!error.isEmpty());
+ descendant->setInstantiationError(error.isolate(), error.v8Value());
+ descendant->clearRecord();
+ } else {
+ // Step 7.2. Otherwise, set script's instantiation state to
+ // "instantiated".
+ descendant->setInstantiationSuccess();
+ }
+ }
+
+ // Step 8. Asynchronously complete this algorithm with descendants result.
+ advanceState(State::Finished);
+}
+
+HeapHashSet<Member<ModuleScript>>
+ModuleTreeLinker::uninstantiatedInclusiveDescendants() {
+ // https://html.spec.whatwg.org/multipage/webappapis.html#uninstantiated-inclusive-descendant-module-scripts
+ // Step 1. Let moduleMap be script's settings object's module map.
+ // Note: Modulator is our "settings object".
+ // Note: We won't reference the ModuleMap directly here to aid testing.
+
+ // Step 2. Let stack be the stack « script ».
+ // TODO(kouhei): Make stack a HeapLinkedHashSet for O(1) lookups.
+ HeapDeque<Member<ModuleScript>> stack;
+ stack.push_front(m_moduleScript);
+
+ // Step 3. Let inclusive descendants be an empty set.
+ // Note: We use unordered set here as the order is not observable from web
+ // platform.
+ // This is allowed per spec: https://infra.spec.whatwg.org/#sets
+ HeapHashSet<Member<ModuleScript>> inclusiveDescendants;
+
+ // Step 4. While stack is not empty:
+ while (!stack.isEmpty()) {
+ // Step 4.1. Let current the result of popping from stack.
+ ModuleScript* current = stack.takeFirst();
+
+ // Step 4.2. If inclusive descendants and stack both do not contain current,
+ // then:
+ if (inclusiveDescendants.contains(current))
+ continue;
+ if (std::find(stack.begin(), stack.end(), current) != stack.end())
+ continue;
+
+ // Step 4.2.1. Append current to inclusive descendants.
+ inclusiveDescendants.insert(current);
+
+ // TODO(kouhei): This implementation is a direct transliteration of the
+ // spec. Omit intermediate vectors at the least.
+
+ // Step 4.2.2. Let child specifiers be the value of current's module
+ // record's [[RequestedModules]] internal slot.
+ Vector<String> childSpecifiers =
+ m_modulator->moduleRequestsFromScriptModule(current->record());
+ // Step 4.2.3. Let child URLs be the list obtained by calling resolve a
+ // module specifier once for each item of child specifiers, given current
+ // and that item. Omit any failures.
+ Vector<KURL> childURLs;
+ for (const auto& childSpecifier : childSpecifiers) {
+ KURL childURL = m_modulator->resolveModuleSpecifier(childSpecifier,
+ current->baseURL());
+ if (childURL.isValid())
+ childURLs.push_back(childURL);
+ }
+ // Step 4.2.4. Let child modules be the list obtained by getting each value
+ // in moduleMap whose key is given by an item of child URLs.
+ HeapVector<Member<ModuleScript>> childModules;
+ for (const auto& childURL : childURLs) {
+ ModuleScript* moduleScript =
+ m_modulator->getFetchedModuleScript(childURL);
+ childModules.push_back(moduleScript);
+ }
+ // Step 4.2.5. For each s in child modules that is not contained by
+ // inclusive descendants, push s onto stack.
+ for (const auto& s : childModules) {
+ if (!inclusiveDescendants.contains(s))
+ stack.push_front(s);
+ }
+ }
+
+ // Step 5. Return a set containing all items of inclusive descendants whose
+ // instantiation state is "uninstantiated".
+ HeapHashSet<Member<ModuleScript>> uninstantiatedSet;
+ for (const auto& script : inclusiveDescendants) {
+ if (script->instantiationState() ==
+ ModuleInstantiationState::Uninstantiated)
+ uninstantiatedSet.insert(script);
+ }
+ return uninstantiatedSet;
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698