1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qv4executablecompilationunit_p.h"
41
42#include <private/qv4engine_p.h>
43#include <private/qv4regexp_p.h>
44#include <private/qv4lookup_p.h>
45#include <private/qv4qmlcontext_p.h>
46#include <private/qv4identifiertable_p.h>
47#include <private/qv4objectproto_p.h>
48#include <private/qqmlengine_p.h>
49#include <private/qv4qobjectwrapper_p.h>
50#include <private/qqmlvaluetypewrapper_p.h>
51#include <private/qqmlscriptdata_p.h>
52#include <private/qv4module_p.h>
53#include <private/qv4compilationunitmapper_p.h>
54#include <private/qml_compile_hash_p.h>
55#include <private/qqmltypewrapper_p.h>
56#include <private/inlinecomponentutils_p.h>
57
58#include <QtQml/qqmlfile.h>
59#include <QtQml/qqmlpropertymap.h>
60
61#include <QtCore/qdir.h>
62#include <QtCore/qstandardpaths.h>
63#include <QtCore/qfileinfo.h>
64#include <QtCore/qscopeguard.h>
65#include <QtCore/qcryptographichash.h>
66#include <QtCore/QScopedValueRollback>
67
68#if defined(QML_COMPILE_HASH)
69# ifdef Q_OS_LINUX
70// Place on a separate section on Linux so it's easier to check from outside
71// what the hash version is.
72__attribute__((section(".qml_compile_hash")))
73# endif
74const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH;
75static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1,
76 "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
77#else
78# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
79#endif
80
81QT_BEGIN_NAMESPACE
82
83namespace QV4 {
84
85ExecutableCompilationUnit::ExecutableCompilationUnit() = default;
86
87ExecutableCompilationUnit::ExecutableCompilationUnit(
88 CompiledData::CompilationUnit &&compilationUnit)
89 : CompiledData::CompilationUnit(std::move(compilationUnit))
90{}
91
92ExecutableCompilationUnit::~ExecutableCompilationUnit()
93{
94 unlink();
95}
96
97QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url)
98{
99 static const QByteArray envCachePath = qgetenv(varName: "QML_DISK_CACHE_PATH");
100
101 const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
102 const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
103 QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
104 fileNameHash.addData(data: localSourcePath.toUtf8());
105 QString directory = envCachePath.isEmpty()
106 ? QStandardPaths::writableLocation(type: QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/")
107 : QString::fromLocal8Bit(str: envCachePath) + QLatin1String("/");
108 QDir::root().mkpath(dirPath: directory);
109 return directory + QString::fromUtf8(str: fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
110}
111
112static QString toString(QV4::ReturnedValue v)
113{
114 Value val = Value::fromReturnedValue(val: v);
115 QString result;
116 if (val.isInt32())
117 result = QLatin1String("int ");
118 else if (val.isDouble())
119 result = QLatin1String("double ");
120 if (val.isEmpty())
121 result += QLatin1String("empty");
122 else
123 result += val.toQStringNoThrow();
124 return result;
125}
126
127static void dumpConstantTable(const StaticValue *constants, uint count)
128{
129 QDebug d = qDebug();
130 d.nospace() << Qt::right;
131 for (uint i = 0; i < count; ++i) {
132 d << qSetFieldWidth(width: 8) << i << qSetFieldWidth(width: 0) << ": "
133 << toString(v: constants[i].asReturnedValue()).toUtf8().constData() << "\n";
134 }
135}
136
137QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
138{
139 this->engine = engine;
140 engine->compilationUnits.insert(n: this);
141
142 Q_ASSERT(!runtimeStrings);
143 Q_ASSERT(data);
144 const quint32 stringCount = totalStringCount();
145 runtimeStrings = (QV4::Heap::String **)malloc(size: stringCount * sizeof(QV4::Heap::String*));
146 // memset the strings to 0 in case a GC run happens while we're within the loop below
147 memset(s: runtimeStrings, c: 0, n: stringCount * sizeof(QV4::Heap::String*));
148 for (uint i = 0; i < stringCount; ++i)
149 runtimeStrings[i] = engine->newString(s: stringAt(index: i));
150
151 runtimeRegularExpressions
152 = new QV4::Value[data->regexpTableSize];
153 // memset the regexps to 0 in case a GC run happens while we're within the loop below
154 memset(s: runtimeRegularExpressions, c: 0,
155 n: data->regexpTableSize * sizeof(QV4::Value));
156 for (uint i = 0; i < data->regexpTableSize; ++i) {
157 const CompiledData::RegExp *re = data->regexpAt(index: i);
158 uint f = re->flags;
159 const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
160 runtimeRegularExpressions[i] = QV4::RegExp::create(
161 engine, pattern: stringAt(index: re->stringIndex), flags);
162 }
163
164 if (data->lookupTableSize) {
165 runtimeLookups = new QV4::Lookup[data->lookupTableSize];
166 memset(s: runtimeLookups, c: 0, n: data->lookupTableSize * sizeof(QV4::Lookup));
167 const CompiledData::Lookup *compiledLookups = data->lookupTable();
168 for (uint i = 0; i < data->lookupTableSize; ++i) {
169 QV4::Lookup *l = runtimeLookups + i;
170
171 CompiledData::Lookup::Type type
172 = CompiledData::Lookup::Type(uint(compiledLookups[i].type_and_flags));
173 if (type == CompiledData::Lookup::Type_Getter)
174 l->getter = QV4::Lookup::getterGeneric;
175 else if (type == CompiledData::Lookup::Type_Setter)
176 l->setter = QV4::Lookup::setterGeneric;
177 else if (type == CompiledData::Lookup::Type_GlobalGetter)
178 l->globalGetter = QV4::Lookup::globalGetterGeneric;
179 else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
180 l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
181 l->nameIndex = compiledLookups[i].nameIndex;
182 }
183 }
184
185 if (data->jsClassTableSize) {
186 runtimeClasses
187 = (QV4::Heap::InternalClass **)malloc(size: data->jsClassTableSize
188 * sizeof(QV4::Heap::InternalClass *));
189 // memset the regexps to 0 in case a GC run happens while we're within the loop below
190 memset(s: runtimeClasses, c: 0,
191 n: data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
192 for (uint i = 0; i < data->jsClassTableSize; ++i) {
193 int memberCount = 0;
194 const CompiledData::JSClassMember *member
195 = data->jsClassAt(idx: i, nMembers: &memberCount);
196 runtimeClasses[i]
197 = engine->internalClasses(icType: QV4::ExecutionEngine::Class_Object);
198 for (int j = 0; j < memberCount; ++j, ++member)
199 runtimeClasses[i]
200 = runtimeClasses[i]->addMember(
201 identifier: engine->identifierTable->asPropertyKey(
202 str: runtimeStrings[member->nameOffset]),
203 data: member->isAccessor
204 ? QV4::Attr_Accessor
205 : QV4::Attr_Data);
206 }
207 }
208
209 runtimeFunctions.resize(asize: data->functionTableSize);
210 for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
211 const QV4::CompiledData::Function *compiledFunction = data->functionAt(idx: i);
212 runtimeFunctions[i] = QV4::Function::create(engine, unit: this, function: compiledFunction);
213 }
214
215 Scope scope(engine);
216 Scoped<InternalClass> ic(scope);
217
218 runtimeBlocks.resize(asize: data->blockTableSize);
219 for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
220 const QV4::CompiledData::Block *compiledBlock = data->blockAt(idx: i);
221 ic = engine->internalClasses(icType: EngineBase::Class_CallContext);
222
223 // first locals
224 const quint32_le *localsIndices = compiledBlock->localsTable();
225 for (quint32 j = 0; j < compiledBlock->nLocals; ++j)
226 ic = ic->addMember(
227 identifier: engine->identifierTable->asPropertyKey(str: runtimeStrings[localsIndices[j]]),
228 data: Attr_NotConfigurable);
229 runtimeBlocks[i] = ic->d();
230 }
231
232 static const bool showCode = qEnvironmentVariableIsSet(varName: "QV4_SHOW_BYTECODE");
233 if (showCode) {
234 qDebug() << "=== Constant table";
235 dumpConstantTable(constants, count: data->constantTableSize);
236 qDebug() << "=== String table";
237 for (uint i = 0, end = totalStringCount(); i < end; ++i)
238 qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
239 qDebug() << "=== Closure table";
240 for (uint i = 0; i < data->functionTableSize; ++i)
241 qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
242 qDebug() << "root function at index "
243 << (data->indexOfRootFunction != -1
244 ? data->indexOfRootFunction : 0);
245 }
246
247 if (data->indexOfRootFunction != -1)
248 return runtimeFunctions[data->indexOfRootFunction];
249 else
250 return nullptr;
251}
252
253Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
254{
255 Q_ASSERT(index < int(data->templateObjectTableSize));
256 if (!templateObjects.size())
257 templateObjects.resize(asize: data->templateObjectTableSize);
258 Heap::Object *o = templateObjects.at(i: index);
259 if (o)
260 return o;
261
262 // create the template object
263 Scope scope(engine);
264 const CompiledData::TemplateObject *t = data->templateObjectAt(idx: index);
265 Scoped<ArrayObject> a(scope, engine->newArrayObject(count: t->size));
266 Scoped<ArrayObject> raw(scope, engine->newArrayObject(count: t->size));
267 ScopedValue s(scope);
268 for (uint i = 0; i < t->size; ++i) {
269 s = runtimeStrings[t->stringIndexAt(i)];
270 a->arraySet(index: i, value: s);
271 s = runtimeStrings[t->rawStringIndexAt(i)];
272 raw->arraySet(index: i, value: s);
273 }
274
275 ObjectPrototype::method_freeze(engine->functionCtor(), thisObject: nullptr, argv: raw, argc: 1);
276 a->defineReadonlyProperty(QStringLiteral("raw"), value: raw);
277 ObjectPrototype::method_freeze(engine->functionCtor(), thisObject: nullptr, argv: a, argc: 1);
278
279 templateObjects[index] = a->objectValue()->d();
280 return templateObjects.at(i: index);
281}
282
283void ExecutableCompilationUnit::unlink()
284{
285 if (engine)
286 nextCompilationUnit.remove();
287
288 if (isRegisteredWithEngine) {
289 Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0));
290 if (qmlEngine)
291 qmlEngine->unregisterInternalCompositeType(compilationUnit: this);
292 QQmlMetaType::unregisterInternalCompositeType(typeIds: {metaTypeId, listMetaTypeId});
293 isRegisteredWithEngine = false;
294 }
295
296 propertyCaches.clear();
297
298 if (runtimeLookups) {
299 for (uint i = 0; i < data->lookupTableSize; ++i)
300 runtimeLookups[i].releasePropertyCache();
301 }
302
303 dependentScripts.clear();
304
305 typeNameCache = nullptr;
306
307 qDeleteAll(c: resolvedTypes);
308 resolvedTypes.clear();
309
310 engine = nullptr;
311 qmlEngine = nullptr;
312
313 delete [] runtimeLookups;
314 runtimeLookups = nullptr;
315
316 for (QV4::Function *f : qAsConst(t&: runtimeFunctions))
317 f->destroy();
318 runtimeFunctions.clear();
319
320 free(ptr: runtimeStrings);
321 runtimeStrings = nullptr;
322 delete [] runtimeRegularExpressions;
323 runtimeRegularExpressions = nullptr;
324 free(ptr: runtimeClasses);
325 runtimeClasses = nullptr;
326}
327
328void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack)
329{
330 if (runtimeStrings) {
331 for (uint i = 0, end = totalStringCount(); i < end; ++i)
332 if (runtimeStrings[i])
333 runtimeStrings[i]->mark(markStack);
334 }
335 if (runtimeRegularExpressions) {
336 for (uint i = 0; i < data->regexpTableSize; ++i)
337 Value::fromStaticValue(staticValue: runtimeRegularExpressions[i]).mark(markStack);
338 }
339 if (runtimeClasses) {
340 for (uint i = 0; i < data->jsClassTableSize; ++i)
341 if (runtimeClasses[i])
342 runtimeClasses[i]->mark(markStack);
343 }
344 for (QV4::Function *f : qAsConst(t&: runtimeFunctions))
345 if (f && f->internalClass)
346 f->internalClass->mark(markStack);
347 for (QV4::Heap::InternalClass *c : qAsConst(t&: runtimeBlocks))
348 if (c)
349 c->mark(markStack);
350
351 for (QV4::Heap::Object *o : qAsConst(t&: templateObjects))
352 if (o)
353 o->mark(markStack);
354
355 if (runtimeLookups) {
356 for (uint i = 0; i < data->lookupTableSize; ++i)
357 runtimeLookups[i].markObjects(stack: markStack);
358 }
359
360 if (auto mod = module())
361 mod->mark(markStack);
362}
363
364IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
365{
366 IdentifierHash namedObjectCache(engine);
367 const CompiledData::Object *component = objectAt(index: componentObjectIndex);
368 const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
369 for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
370 const CompiledData::Object *namedObject = objectAt(index: *namedObjectIndexPtr);
371 namedObjectCache.add(str: runtimeStrings[namedObject->idNameIndex], value: namedObject->id);
372 }
373 return *namedObjectsPerComponentCache.insert(akey: componentObjectIndex, avalue: namedObjectCache);
374}
375
376void ExecutableCompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine, CompositeMetaTypeIds typeIds)
377{
378 this->qmlEngine = qmlEngine;
379
380 // Add to type registry of composites
381 if (propertyCaches.needsVMEMetaObject(/*root object*/index: 0)) {
382 // typeIds is only valid for types that have references to themselves.
383 if (!typeIds.isValid())
384 typeIds = QQmlMetaType::registerInternalCompositeType(className: rootPropertyCache()->className());
385 metaTypeId = typeIds.id;
386 listMetaTypeId = typeIds.listId;
387 qmlEngine->registerInternalCompositeType(compilationUnit: this);
388
389 } else {
390 const QV4::CompiledData::Object *obj = objectAt(/*root object*/index: 0);
391 auto *typeRef = resolvedTypes.value(akey: obj->inheritedTypeNameIndex);
392 Q_ASSERT(typeRef);
393 if (const auto compilationUnit = typeRef->compilationUnit()) {
394 metaTypeId = compilationUnit->metaTypeId;
395 listMetaTypeId = compilationUnit->listMetaTypeId;
396 } else {
397 metaTypeId = typeRef->type.typeId();
398 listMetaTypeId = typeRef->type.qListTypeId();
399 }
400 }
401
402 // Collect some data for instantiation later.
403 using namespace icutils;
404 std::vector<QV4::CompiledData::InlineComponent> allICs {};
405 for (int i=0; i != objectCount(); ++i) {
406 const CompiledObject *obj = objectAt(index: i);
407 for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
408 allICs.push_back(x: *it);
409 }
410 }
411 std::vector<Node> nodes;
412 nodes.resize(new_size: allICs.size());
413 std::iota(first: nodes.begin(), last: nodes.end(), value: 0);
414 AdjacencyList adjacencyList;
415 adjacencyList.resize(new_size: nodes.size());
416 fillAdjacencyListForInlineComponents(objectContainer: this, adjacencyList, nodes, allICs);
417 bool hasCycle = false;
418 auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
419 Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator
420
421 // We need to first iterate over all inline components, as the containing component might create instances of them
422 // and in that case we need to add its object count
423 for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
424 const auto &ic = allICs.at(n: nodeIt->index);
425 int lastICRoot = ic.objectIndex;
426 for (int i = ic.objectIndex; i<objectCount(); ++i) {
427 const QV4::CompiledData::Object *obj = objectAt(index: i);
428 bool leftCurrentInlineComponent =
429 (i != lastICRoot && obj->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
430 || !(obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent);
431 if (leftCurrentInlineComponent)
432 break;
433 inlineComponentData[lastICRoot].totalBindingCount += obj->nBindings;
434
435 if (auto *typeRef = resolvedTypes.value(akey: obj->inheritedTypeNameIndex)) {
436 if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1)
437 ++inlineComponentData[lastICRoot].totalParserStatusCount;
438
439 ++inlineComponentData[lastICRoot].totalObjectCount;
440 if (const auto compilationUnit = typeRef->compilationUnit()) {
441 // if the type is an inline component type, we have to extract the information from it
442 // This requires that inline components are visited in the correct order
443 auto icRoot = compilationUnit->icRoot;
444 if (typeRef->type.isInlineComponentType()) {
445 icRoot = typeRef->type.inlineComponendId();
446 }
447 QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot};
448 inlineComponentData[lastICRoot].totalBindingCount += compilationUnit->totalBindingsCount();
449 inlineComponentData[lastICRoot].totalParserStatusCount += compilationUnit->totalParserStatusCount();
450 inlineComponentData[lastICRoot].totalObjectCount += compilationUnit->totalObjectCount();
451 }
452 }
453 }
454 }
455 int bindingCount = 0;
456 int parserStatusCount = 0;
457 int objectCount = 0;
458 for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
459 const QV4::CompiledData::Object *obj = objectAt(index: i);
460 if (obj->flags & QV4::CompiledData::Object::InPartOfInlineComponent) {
461 continue;
462 }
463 bindingCount += obj->nBindings;
464 if (auto *typeRef = resolvedTypes.value(akey: obj->inheritedTypeNameIndex)) {
465 if (typeRef->type.isValid() && typeRef->type.parserStatusCast() != -1)
466 ++parserStatusCount;
467 ++objectCount;
468 if (const auto compilationUnit = typeRef->compilationUnit()) {
469 auto icRoot = compilationUnit->icRoot;
470 if (typeRef->type.isInlineComponentType()) {
471 icRoot = typeRef->type.inlineComponendId();
472 }
473 QScopedValueRollback<int> rollback {compilationUnit->icRoot, icRoot};
474 bindingCount += compilationUnit->totalBindingsCount();
475 parserStatusCount += compilationUnit->totalParserStatusCount();
476 objectCount += compilationUnit->totalObjectCount();
477 }
478 }
479 }
480
481 m_totalBindingsCount = bindingCount;
482 m_totalParserStatusCount = parserStatusCount;
483 m_totalObjectCount = objectCount;
484}
485
486int ExecutableCompilationUnit::totalBindingsCount() const {
487 if (icRoot == -1)
488 return m_totalBindingsCount;
489 return inlineComponentData[icRoot].totalBindingCount;
490}
491
492int ExecutableCompilationUnit::totalObjectCount() const {
493 if (icRoot == -1)
494 return m_totalObjectCount;
495 return inlineComponentData[icRoot].totalObjectCount;
496}
497
498int ExecutableCompilationUnit::totalParserStatusCount() const {
499 if (icRoot == -1)
500 return m_totalParserStatusCount;
501 return inlineComponentData[icRoot].totalParserStatusCount;
502}
503
504bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const
505{
506 if (!dependencyHasher) {
507 for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
508 if (data->dependencyMD5Checksum[i] != 0)
509 return false;
510 }
511 return true;
512 }
513 const QByteArray checksum = dependencyHasher();
514 return checksum.size() == sizeof(data->dependencyMD5Checksum)
515 && memcmp(s1: data->dependencyMD5Checksum, s2: checksum.constData(),
516 n: sizeof(data->dependencyMD5Checksum)) == 0;
517}
518
519CompositeMetaTypeIds ExecutableCompilationUnit::typeIdsForComponent(int objectid) const
520{
521 if (objectid == 0)
522 return {metaTypeId, listMetaTypeId};
523 return inlineComponentData[objectid].typeIds;
524}
525
526QStringList ExecutableCompilationUnit::moduleRequests() const
527{
528 QStringList requests;
529 requests.reserve(alloc: data->moduleRequestTableSize);
530 for (uint i = 0; i < data->moduleRequestTableSize; ++i)
531 requests << stringAt(index: data->moduleRequestTable()[i]);
532 return requests;
533}
534
535Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
536{
537 if (isESModule() && module())
538 return module();
539
540 if (data->indexOfRootFunction < 0)
541 return nullptr;
542
543 if (!this->engine)
544 linkToEngine(engine);
545
546 Scope scope(engine);
547 Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(args: engine, args: this));
548
549 if (isESModule())
550 setModule(module->d());
551
552 for (const QString &request: moduleRequests()) {
553 auto dependentModuleUnit = engine->loadModule(url: QUrl(request), referrer: this);
554 if (engine->hasException)
555 return nullptr;
556 dependentModuleUnit->instantiate(engine);
557 }
558
559 ScopedString importName(scope);
560
561 const uint importCount = data->importEntryTableSize;
562 if (importCount > 0) {
563 imports = new const StaticValue *[importCount];
564 memset(s: imports, c: 0, n: importCount * sizeof(StaticValue *));
565 }
566 for (uint i = 0; i < importCount; ++i) {
567 const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
568 auto dependentModuleUnit = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this);
569 importName = runtimeStrings[entry.importName];
570 const Value *valuePtr = dependentModuleUnit->resolveExport(exportName: importName);
571 if (!valuePtr) {
572 QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
573 referenceErrorMessage += importName->toQString();
574 engine->throwReferenceError(value: referenceErrorMessage, fileName: fileName(), lineNumber: entry.location.line, column: entry.location.column);
575 return nullptr;
576 }
577 imports[i] = valuePtr;
578 }
579
580 for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
581 const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
582 auto dependentModuleUnit = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this);
583 if (!dependentModuleUnit)
584 return nullptr;
585
586 ScopedString importName(scope, runtimeStrings[entry.importName]);
587 if (!dependentModuleUnit->resolveExport(exportName: importName)) {
588 QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
589 referenceErrorMessage += importName->toQString();
590 engine->throwReferenceError(value: referenceErrorMessage, fileName: fileName(), lineNumber: entry.location.line, column: entry.location.column);
591 return nullptr;
592 }
593 }
594
595 return module->d();
596}
597
598const Value *ExecutableCompilationUnit::resolveExportRecursively(
599 QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
600{
601 if (!module())
602 return nullptr;
603
604 for (const auto &entry: *resolveSet)
605 if (entry.module == this && entry.exportName->isEqualTo(other: exportName))
606 return nullptr;
607
608 (*resolveSet) << ResolveSetEntry(this, exportName);
609
610 if (exportName->toQString() == QLatin1String("*"))
611 return &module()->self;
612
613 Scope scope(engine);
614
615 if (auto localExport = lookupNameInExportTable(
616 firstExportEntry: data->localExportEntryTable(), tableSize: data->localExportEntryTableSize, name: exportName)) {
617 ScopedString localName(scope, runtimeStrings[localExport->localName]);
618 uint index = module()->scope->internalClass->indexOfValueOrGetter(id: localName->toPropertyKey());
619 if (index == UINT_MAX)
620 return nullptr;
621 if (index >= module()->scope->locals.size)
622 return &(imports[index - module()->scope->locals.size]->asValue<Value>());
623 return &module()->scope->locals[index];
624 }
625
626 if (auto indirectExport = lookupNameInExportTable(
627 firstExportEntry: data->indirectExportEntryTable(), tableSize: data->indirectExportEntryTableSize, name: exportName)) {
628 auto dependentModuleUnit = engine->loadModule(url: urlAt(index: indirectExport->moduleRequest), referrer: this);
629 if (!dependentModuleUnit)
630 return nullptr;
631 ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
632 return dependentModuleUnit->resolveExportRecursively(exportName: importName, resolveSet);
633 }
634
635
636 if (exportName->toQString() == QLatin1String("default"))
637 return nullptr;
638
639 const Value *starResolution = nullptr;
640
641 for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
642 const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
643 auto dependentModuleUnit = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this);
644 if (!dependentModuleUnit)
645 return nullptr;
646
647 const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet);
648 // ### handle ambiguous
649 if (resolution) {
650 if (!starResolution) {
651 starResolution = resolution;
652 continue;
653 }
654 if (resolution != starResolution)
655 return nullptr;
656 }
657 }
658
659 return starResolution;
660}
661
662const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable(
663 const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
664{
665 const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
666 auto matchingExport = std::lower_bound(first: firstExportEntry, last: lastExportEntry, val: name, comp: [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
667 return stringAt(index: lhs.exportName) < name->toQString();
668 });
669 if (matchingExport == lastExportEntry || stringAt(index: matchingExport->exportName) != name->toQString())
670 return nullptr;
671 return matchingExport;
672}
673
674void ExecutableCompilationUnit::getExportedNamesRecursively(
675 QStringList *names, QVector<const ExecutableCompilationUnit*> *exportNameSet,
676 bool includeDefaultExport) const
677{
678 if (exportNameSet->contains(t: this))
679 return;
680 exportNameSet->append(t: this);
681
682 const auto append = [names, includeDefaultExport](const QString &name) {
683 if (!includeDefaultExport && name == QLatin1String("default"))
684 return;
685 names->append(t: name);
686 };
687
688 for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
689 const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
690 append(stringAt(index: entry.exportName));
691 }
692
693 for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
694 const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
695 append(stringAt(index: entry.exportName));
696 }
697
698 for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
699 const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
700 auto dependentModuleUnit = engine->loadModule(url: urlAt(index: entry.moduleRequest), referrer: this);
701 if (!dependentModuleUnit)
702 return;
703 dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false);
704 }
705}
706
707void ExecutableCompilationUnit::evaluate()
708{
709 QV4::Scope scope(engine);
710 QV4::Scoped<Module> mod(scope, module());
711 mod->evaluate();
712}
713
714void ExecutableCompilationUnit::evaluateModuleRequests()
715{
716 for (const QString &request: moduleRequests()) {
717 auto dependentModuleUnit = engine->loadModule(url: QUrl(request), referrer: this);
718 if (engine->hasException)
719 return;
720 dependentModuleUnit->evaluate();
721 if (engine->hasException)
722 return;
723 }
724}
725
726bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
727{
728 if (!QQmlFile::isLocalFile(url)) {
729 *errorString = QStringLiteral("File has to be a local file.");
730 return false;
731 }
732
733 const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
734 QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
735
736 const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
737 for (const QString &cachePath : cachePaths) {
738 CompiledData::Unit *mappedUnit = cacheFile->get(cacheFilePath: cachePath, sourceTimeStamp, errorString);
739 if (!mappedUnit)
740 continue;
741
742 const CompiledData::Unit * const oldDataPtr
743 = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data
744 : nullptr;
745 const CompiledData::Unit *oldData = data;
746 auto dataPtrRevert = qScopeGuard(f: [this, oldData](){
747 setUnitData(unitData: oldData);
748 });
749 setUnitData(unitData: mappedUnit);
750
751 if (data->sourceFileIndex != 0
752 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(index: data->sourceFileIndex))) {
753 *errorString = QStringLiteral("QML source file has moved to a different location.");
754 continue;
755 }
756
757 dataPtrRevert.dismiss();
758 free(ptr: const_cast<CompiledData::Unit*>(oldDataPtr));
759 backingFile.reset(other: cacheFile.take());
760 return true;
761 }
762
763 return false;
764}
765
766bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
767{
768 if (data->sourceTimeStamp == 0) {
769 *errorString = QStringLiteral("Missing time stamp for source file");
770 return false;
771 }
772
773 if (!QQmlFile::isLocalFile(url: unitUrl)) {
774 *errorString = QStringLiteral("File has to be a local file.");
775 return false;
776 }
777
778 return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
779 writer: [&unitUrl, errorString](const char *data, quint32 size) {
780 return CompiledData::SaveableUnitPointer::writeDataToFile(outputFileName: localCacheFilePath(url: unitUrl), data,
781 size, errorString);
782 });
783}
784
785/*!
786Returns the property cache, if one alread exists. The cache is not referenced.
787*/
788QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const
789{
790 if (type.isValid())
791 return typePropertyCache;
792 else
793 return m_compilationUnit->rootPropertyCache();
794}
795
796/*!
797Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
798*/
799QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
800{
801 if (typePropertyCache) {
802 return typePropertyCache;
803 } else if (type.isValid()) {
804 typePropertyCache = QQmlEnginePrivate::get(e: engine)->cache(metaObject: type.metaObject(), minorVersion);
805 return typePropertyCache;
806 } else {
807 Q_ASSERT(m_compilationUnit);
808 return m_compilationUnit->rootPropertyCache();
809 }
810}
811
812bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine)
813{
814 if (type.isValid() && !type.isInlineComponentType()) {
815 bool ok = false;
816 hash->addData(data: createPropertyCache(engine)->checksum(ok: &ok));
817 return ok;
818 }
819 if (!m_compilationUnit)
820 return false;
821 hash->addData(data: m_compilationUnit->data->md5Checksum,
822 length: sizeof(m_compilationUnit->data->md5Checksum));
823 return true;
824}
825
826template <typename T>
827bool qtTypeInherits(const QMetaObject *mo) {
828 while (mo) {
829 if (mo == &T::staticMetaObject)
830 return true;
831 mo = mo->superClass();
832 }
833 return false;
834}
835
836void ResolvedTypeReference::doDynamicTypeCheck()
837{
838 const QMetaObject *mo = nullptr;
839 if (typePropertyCache)
840 mo = typePropertyCache->firstCppMetaObject();
841 else if (type.isValid())
842 mo = type.metaObject();
843 else if (m_compilationUnit)
844 mo = m_compilationUnit->rootPropertyCache()->firstCppMetaObject();
845 isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo);
846}
847
848bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const
849{
850 for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
851 if (!it.value()->addToHash(hash, engine))
852 return false;
853 }
854
855 return true;
856}
857
858QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
859{
860 using namespace CompiledData;
861 switch (binding->type) {
862 case Binding::Type_Script:
863 case Binding::Type_String:
864 return stringAt(index: binding->stringIndex);
865 case Binding::Type_Null:
866 return QStringLiteral("null");
867 case Binding::Type_Boolean:
868 return binding->value.b ? QStringLiteral("true") : QStringLiteral("false");
869 case Binding::Type_Number:
870 return QString::number(bindingValueAsNumber(binding), f: 'g', prec: QLocale::FloatingPointShortest);
871 case Binding::Type_Invalid:
872 return QString();
873#if !QT_CONFIG(translation)
874 case Binding::Type_TranslationById:
875 case Binding::Type_Translation:
876 return stringAt(
877 data->translations()[binding->value.translationDataIndex].stringIndex);
878#else
879 case Binding::Type_TranslationById: {
880 const TranslationData &translation
881 = data->translations()[binding->value.translationDataIndex];
882 QByteArray id = stringAt(index: translation.stringIndex).toUtf8();
883 return qtTrId(id: id.constData(), n: translation.number);
884 }
885 case Binding::Type_Translation: {
886 const TranslationData &translation
887 = data->translations()[binding->value.translationDataIndex];
888 // This code must match that in the qsTr() implementation
889 const QString &path = fileName();
890 int lastSlash = path.lastIndexOf(c: QLatin1Char('/'));
891 QStringRef context = (lastSlash > -1) ? path.midRef(position: lastSlash + 1, n: path.length() - lastSlash - 5)
892 : QStringRef();
893 QByteArray contextUtf8 = context.toUtf8();
894 QByteArray comment = stringAt(index: translation.commentIndex).toUtf8();
895 QByteArray text = stringAt(index: translation.stringIndex).toUtf8();
896 return QCoreApplication::translate(context: contextUtf8.constData(), key: text.constData(),
897 disambiguation: comment.constData(), n: translation.number);
898 }
899#endif
900 default:
901 break;
902 }
903 return QString();
904}
905
906QString ExecutableCompilationUnit::bindingValueAsScriptString(
907 const CompiledData::Binding *binding) const
908{
909 return (binding->type == CompiledData::Binding::Type_String)
910 ? CompiledData::Binding::escapedString(string: stringAt(index: binding->stringIndex))
911 : bindingValueAsString(binding);
912}
913
914bool ExecutableCompilationUnit::verifyHeader(
915 const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString)
916{
917 if (strncmp(s1: unit->magic, s2: CompiledData::magic_str, n: sizeof(unit->magic))) {
918 *errorString = QStringLiteral("Magic bytes in the header do not match");
919 return false;
920 }
921
922 if (unit->version != quint32(QV4_DATA_STRUCTURE_VERSION)) {
923 *errorString = QString::fromUtf8(str: "V4 data structure version mismatch. Found %1 expected %2")
924 .arg(a: unit->version, fieldWidth: 0, base: 16).arg(QV4_DATA_STRUCTURE_VERSION, fieldWidth: 0, base: 16);
925 return false;
926 }
927
928 if (unit->qtVersion != quint32(QT_VERSION)) {
929 *errorString = QString::fromUtf8(str: "Qt version mismatch. Found %1 expected %2")
930 .arg(a: unit->qtVersion, fieldWidth: 0, base: 16).arg(QT_VERSION, fieldWidth: 0, base: 16);
931 return false;
932 }
933
934 if (unit->sourceTimeStamp) {
935 // Files from the resource system do not have any time stamps, so fall back to the application
936 // executable.
937 if (!expectedSourceTimeStamp.isValid())
938 expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
939
940 if (expectedSourceTimeStamp.isValid()
941 && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) {
942 *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
943 return false;
944 }
945 }
946
947#if defined(QML_COMPILE_HASH)
948 if (qstrcmp(str1: qml_compile_hash, str2: unit->libraryVersionHash) != 0) {
949 *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match");
950 return false;
951 }
952#else
953#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
954#endif
955 return true;
956}
957
958} // namespace QV4
959
960QT_END_NAMESPACE
961

source code of qtdeclarative/src/qml/jsruntime/qv4executablecompilationunit.cpp