Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlscriptblob.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <private/qqmlengine_p.h>
5#include <private/qqmlirbuilder_p.h>
6#include <private/qqmlscriptblob_p.h>
7#include <private/qqmlsourcecoordinate_p.h>
8#include <private/qqmlcontextdata_p.h>
9#include <private/qv4runtimecodegen_p.h>
10#include <private/qv4script_p.h>
11
12#include <QtCore/qloggingcategory.h>
13
15
16Q_LOGGING_CATEGORY(DBG_DISK_CACHE, "qt.qml.diskcache")
17
18QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader, IsESModule isESModule)
19 : QQmlNotifyingBlob(url, JavaScriptFile, loader)
20 , m_isModule(isESModule == IsESModule::Yes)
21{
22}
23
24QQmlScriptBlob::~QQmlScriptBlob()
25{
26}
27
28void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
29{
30 assertTypeLoaderThread();
31
32 if (data.isCacheable()) {
33 if (auto unit = QQmlMetaType::obtainCompilationUnit(url())) {
34 initializeFromCompilationUnit(std::move(unit));
35 return;
36 }
37
38 if (m_typeLoader->readCacheFile()) {
39 auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>();
40 QString error;
41 if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
42 initializeFromCompilationUnit(std::move(unit));
43 return;
44 } else {
45 qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString()
46 << "from disk cache:" << error;
47 }
48 }
49 }
50
51 if (!data.exists()) {
52 if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch)
53 setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile"));
54 else
55 setError(QQmlTypeLoader::tr("No such file or directory"));
56 return;
57 }
58
59 QString error;
60 QString source = data.readAll(&error);
61 if (!error.isEmpty()) {
62 setError(error);
63 return;
64 }
65
66 QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
67
68 if (m_isModule) {
69 QList<QQmlJS::DiagnosticMessage> diagnostics;
70 unit = QV4::Compiler::Codegen::compileModule(
71 m_typeLoader->isDebugging(), urlString(), source, data.sourceTimeStamp(),
72 &diagnostics);
73 QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics);
74 if (!errors.isEmpty()) {
75 setError(errors);
76 return;
77 }
78 } else {
79 QmlIR::Document irUnit(urlString(), finalUrlString(), m_typeLoader->isDebugging());
80
81 irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
82
83 QmlIR::ScriptDirectivesCollector collector(&irUnit);
84 irUnit.jsParserEngine.setDirectives(&collector);
85
86 QList<QQmlError> errors;
87 const QString fragment = finalUrl().fragment();
88 irUnit.javaScriptCompilationUnit = QV4::Script::precompile(
89 &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(),
90 source, &errors, fragment == QLatin1String("global")
91 ? QV4::Compiler::ContextType::Global
92 : QV4::Compiler::ContextType::ScriptImportedByQML,
93 fragment == QLatin1String("include")
94 ? QV4::Script::InheritContext::Yes
95 : QV4::Script::InheritContext::No);
96
97 source.clear();
98 if (!errors.isEmpty()) {
99 setError(errors);
100 return;
101 }
102
103 QmlIR::QmlUnitGenerator qmlGenerator;
104 qmlGenerator.generate(irUnit);
105 unit = std::move(irUnit.javaScriptCompilationUnit);
106 }
107
108 if (m_typeLoader->writeCacheFile()) {
109 QString errorString;
110 if (unit->saveToDisk(url(), &errorString)) {
111 QString error;
112 if (!unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
113 // ignore error, keep using the in-memory compilation unit.
114 }
115 } else {
116 qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of"
117 << unit->fileName() << "to disk:" << errorString;
118 }
119 }
120
121 initializeFromCompilationUnit(std::move(unit));
122}
123
124void QQmlScriptBlob::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *cachedUnit)
125{
126 assertTypeLoaderThread();
127 initializeFromCompilationUnit(QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(
128 cachedUnit->qmlData, cachedUnit->aotCompiledFunctions, urlString(), finalUrlString()));
129}
130
131void QQmlScriptBlob::done()
132{
133 assertTypeLoaderThread();
134
135 if (isError())
136 return;
137
138 // Check all script dependencies for errors
139 for (int ii = 0; ii < m_scripts.size(); ++ii) {
140 const ScriptReference &script = m_scripts.at(ii);
141 // We would like to assert on isCompleteOrError() here, but since ECMAScript dependencies
142 // can be cyclic, we need to live with certain scripts formally not being complete.
143 // However, since we only omit the dependency if the compilation unit already exists when
144 // loading, we will still catch all errors here.
145 if (script.script->isError()) {
146 QList<QQmlError> errors = script.script->errors();
147 QQmlError error;
148 error.setUrl(url());
149 error.setLine(qmlConvertSourceCoordinate<quint32, int>(script.location.line()));
150 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(script.location.column()));
151 error.setDescription(QQmlTypeLoader::tr("Script %1 unavailable").arg(script.script->urlString()));
152 errors.prepend(error);
153 setError(errors);
154 return;
155 }
156 }
157
158 if (!m_isModule) {
159 m_scriptData->typeNameCache.adopt(new QQmlTypeNameCache(m_importCache));
160
161 QSet<QString> ns;
162
163 for (int scriptIndex = 0; scriptIndex < m_scripts.size(); ++scriptIndex) {
164 const ScriptReference &script = m_scripts.at(scriptIndex);
165
166 m_scriptData->scripts.append(script.script->scriptData());
167
168 if (!script.nameSpace.isNull()) {
169 if (!ns.contains(script.nameSpace)) {
170 ns.insert(script.nameSpace);
171 m_scriptData->typeNameCache->add(script.nameSpace);
172 }
173 }
174 m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
175 }
176
177 m_importCache->populateCache(m_scriptData->typeNameCache.data());
178 }
179
180 if (auto cu = m_scriptData->compilationUnit()) {
181 cu->qmlType = QQmlMetaType::findCompositeType(url(), cu, QQmlMetaType::JavaScript);
182 for (const auto &script : std::as_const(m_scripts))
183 cu->dependentScripts.append(script.script->scriptData());
184 QQmlMetaType::registerInternalCompositeType(cu);
185 }
186
187 m_scripts.clear();
188}
189
190QString QQmlScriptBlob::stringAt(int index) const
191{
192 return m_scriptData->m_precompiledScript->stringAt(index);
193}
194
195void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
196{
197 assertTypeLoaderThread();
198
199 ScriptReference ref;
200 ref.script = blob;
201 ref.location = location;
202 ref.qualifier = qualifier;
203 ref.nameSpace = nameSpace;
204
205 m_scripts << ref;
206}
207
208void QQmlScriptBlob::initializeFromCompilationUnit(
209 QQmlRefPointer<QV4::CompiledData::CompilationUnit> &&unit)
210{
211 assertTypeLoaderThread();
212 Q_ASSERT(!m_scriptData);
213 Q_ASSERT(unit);
214
215 m_scriptData.adopt(new QQmlScriptData());
216 m_scriptData->url = finalUrl();
217 m_scriptData->urlString = finalUrlString();
218 m_scriptData->m_precompiledScript = unit;
219
220 m_importCache->setBaseUrl(finalUrl(), finalUrlString());
221
222 if (!m_isModule) {
223 QList<QQmlError> errors;
224 for (quint32 i = 0, count = unit->importCount(); i < count; ++i) {
225 const QV4::CompiledData::Import *import = unit->importAt(i);
226 if (!addImport(import, {}, &errors)) {
227 Q_ASSERT(errors.size());
228 QQmlError error(errors.takeFirst());
229 error.setUrl(m_importCache->baseUrl());
230 error.setLine(import->location.line());
231 error.setColumn(import->location.column());
232 errors.prepend(error); // put it back on the list after filling out information.
233 setError(errors);
234 return;
235 }
236 }
237 }
238
239 const QStringList moduleRequests = unit->moduleRequests();
240 for (const QString &request: moduleRequests) {
241 const QUrl relativeRequest(request);
242 QUrl absoluteRequest = unit->finalUrl().resolved(relativeRequest);
243 if (!request.endsWith(QLatin1String(".mjs")))
244 absoluteRequest.setFragment(QLatin1String("module"));
245 QQmlRefPointer<QQmlScriptBlob> absoluteBlob
246 = typeLoader()->getScript(absoluteRequest, relativeRequest);
247 if (!absoluteBlob->m_scriptData || !absoluteBlob->m_scriptData->m_precompiledScript)
248 addDependency(absoluteBlob.data());
249 scriptImported(
250 absoluteBlob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(),
251 /*namespace*/QString());
252 }
253}
254
255QT_END_NAMESPACE
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")