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