Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qv4executablecompilationunit.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 "qml/qqmlprivate.h"
5#include "qv4engine_p.h"
7
8#include <private/qv4engine_p.h>
9#include <private/qv4regexp_p.h>
10#include <private/qv4lookup_p.h>
11#include <private/qv4qmlcontext_p.h>
12#include <private/qv4identifiertable_p.h>
13#include <private/qv4objectproto_p.h>
14#include <private/qqmlengine_p.h>
15#include <private/qv4qobjectwrapper_p.h>
16#include <private/qqmlvaluetypewrapper_p.h>
17#include <private/qqmlscriptdata_p.h>
18#include <private/qv4module_p.h>
19#include <private/qv4compilationunitmapper_p.h>
20#include <private/qml_compile_hash_p.h>
21#include <private/qqmltypewrapper_p.h>
22#include <private/inlinecomponentutils_p.h>
23#include <private/qv4resolvedtypereference_p.h>
24#include <private/qv4objectiterator_p.h>
25
26#include <QtQml/qqmlfile.h>
27#include <QtQml/qqmlpropertymap.h>
28
29#include <QtCore/qdir.h>
30#include <QtCore/qstandardpaths.h>
31#include <QtCore/qfileinfo.h>
32#include <QtCore/qscopeguard.h>
33#include <QtCore/qcryptographichash.h>
34#include <QtCore/QScopedValueRollback>
35
36static_assert(QV4::CompiledData::QmlCompileHashSpace > QML_COMPILE_HASH_LENGTH);
37
38#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
39# ifdef Q_OS_LINUX
40// Place on a separate section on Linux so it's easier to check from outside
41// what the hash version is.
42__attribute__((section(".qml_compile_hash")))
43# endif
44const char qml_compile_hash[QV4::CompiledData::QmlCompileHashSpace] = QML_COMPILE_HASH;
45static_assert(sizeof(QV4::CompiledData::Unit::libraryVersionHash) > QML_COMPILE_HASH_LENGTH,
46 "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
47#else
48# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
49#endif
50
52
53namespace QV4 {
54
55ExecutableCompilationUnit::ExecutableCompilationUnit() = default;
56
57ExecutableCompilationUnit::ExecutableCompilationUnit(
58 CompiledData::CompilationUnit &&compilationUnit)
59 : CompiledData::CompilationUnit(std::move(compilationUnit))
60{}
61
62ExecutableCompilationUnit::~ExecutableCompilationUnit()
63{
64 unlink();
65}
66
67QString ExecutableCompilationUnit::localCacheFilePath(const QUrl &url)
68{
69 static const QByteArray envCachePath = qgetenv("QML_DISK_CACHE_PATH");
70
71 const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
72 const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
74 fileNameHash.addData(localSourcePath.toUtf8());
75 QString directory = envCachePath.isEmpty()
77 : QString::fromLocal8Bit(envCachePath) + QLatin1String("/");
79 return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
80}
81
83{
84 Value val = Value::fromReturnedValue(v);
86 if (val.isInt32())
87 result = QLatin1String("int ");
88 else if (val.isDouble())
89 result = QLatin1String("double ");
90 if (val.isEmpty())
91 result += QLatin1String("empty");
92 else
93 result += val.toQStringNoThrow();
94 return result;
95}
96
97static void dumpConstantTable(const StaticValue *constants, uint count)
98{
99 QDebug d = qDebug();
100 d.nospace() << Qt::right;
101 for (uint i = 0; i < count; ++i) {
102 d << qSetFieldWidth(8) << i << qSetFieldWidth(0) << ": "
103 << toString(constants[i].asReturnedValue()).toUtf8().constData() << "\n";
104 }
105}
106
107QV4::Function *ExecutableCompilationUnit::linkToEngine(ExecutionEngine *engine)
108{
109 this->engine = engine;
110 engine->compilationUnits.insert(this);
111
112 Q_ASSERT(!runtimeStrings);
113 Q_ASSERT(data);
114 const quint32 stringCount = totalStringCount();
115 // strings need to be 0 in case a GC run happens while we're within the loop below
116 runtimeStrings = (QV4::Heap::String **)calloc(stringCount, sizeof(QV4::Heap::String*));
117 for (uint i = 0; i < stringCount; ++i)
118 runtimeStrings[i] = engine->newString(stringAt(i));
119
120 // zero-initialize regexps in case a GC run happens while we're within the loop below
121 runtimeRegularExpressions
122 = new QV4::Value[data->regexpTableSize] {};
123 for (uint i = 0; i < data->regexpTableSize; ++i) {
124 const CompiledData::RegExp *re = data->regexpAt(i);
125 uint f = re->flags();
127 runtimeRegularExpressions[i] = QV4::RegExp::create(
128 engine, stringAt(re->stringIndex()), flags);
129 }
130
131 if (data->lookupTableSize) {
132 runtimeLookups = new QV4::Lookup[data->lookupTableSize];
133 memset(runtimeLookups, 0, data->lookupTableSize * sizeof(QV4::Lookup));
134 const CompiledData::Lookup *compiledLookups = data->lookupTable();
135 for (uint i = 0; i < data->lookupTableSize; ++i) {
136 QV4::Lookup *l = runtimeLookups + i;
137
139 = CompiledData::Lookup::Type(uint(compiledLookups[i].type()));
140 if (type == CompiledData::Lookup::Type_Getter)
142 else if (type == CompiledData::Lookup::Type_Setter)
144 else if (type == CompiledData::Lookup::Type_GlobalGetter)
146 else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter)
147 l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter;
148 l->forCall = compiledLookups[i].mode() == CompiledData::Lookup::Mode_ForCall;
149 l->nameIndex = compiledLookups[i].nameIndex();
150 }
151 }
152
153 if (data->jsClassTableSize) {
154 // zero the regexps with calloc in case a GC run happens while we're within the loop below
155 runtimeClasses
156 = (QV4::Heap::InternalClass **)calloc(data->jsClassTableSize,
157 sizeof(QV4::Heap::InternalClass *));
158
159 for (uint i = 0; i < data->jsClassTableSize; ++i) {
160 int memberCount = 0;
161 const CompiledData::JSClassMember *member
162 = data->jsClassAt(i, &memberCount);
163 runtimeClasses[i]
164 = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
165 for (int j = 0; j < memberCount; ++j, ++member)
166 runtimeClasses[i]
167 = runtimeClasses[i]->addMember(
168 engine->identifierTable->asPropertyKey(
169 runtimeStrings[member->nameOffset()]),
170 member->isAccessor()
173 }
174 }
175
176 runtimeFunctions.resize(data->functionTableSize);
177 static bool ignoreAotCompiledFunctions
178 = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER")
179 || !(engine->diskCacheOptions() & ExecutionEngine::DiskCache::AotNative);
180
181 const QQmlPrivate::AOTCompiledFunction *aotFunction
182 = ignoreAotCompiledFunctions ? nullptr : aotCompiledFunctions;
183
184 auto advanceAotFunction = [&](int i) -> const QQmlPrivate::AOTCompiledFunction * {
185 if (aotFunction) {
186 if (aotFunction->functionPtr) {
187 if (aotFunction->extraData == i)
188 return aotFunction++;
189 } else {
190 aotFunction = nullptr;
191 }
192 }
193 return nullptr;
194 };
195
196 for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
197 const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
198 runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction,
199 advanceAotFunction(i));
200 }
201
202 Scope scope(engine);
203 Scoped<InternalClass> ic(scope);
204
205 runtimeBlocks.resize(data->blockTableSize);
206 for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
207 const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
208 ic = engine->internalClasses(EngineBase::Class_CallContext);
209
210 // first locals
211 const quint32_le *localsIndices = compiledBlock->localsTable();
212 for (quint32 j = 0; j < compiledBlock->nLocals; ++j)
213 ic = ic->addMember(
214 engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[j]]),
216 runtimeBlocks[i] = ic->d();
217 }
218
219 static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
220 if (showCode) {
221 qDebug() << "=== Constant table";
222 dumpConstantTable(constants, data->constantTableSize);
223 qDebug() << "=== String table";
224 for (uint i = 0, end = totalStringCount(); i < end; ++i)
225 qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
226 qDebug() << "=== Closure table";
227 for (uint i = 0; i < data->functionTableSize; ++i)
228 qDebug() << " " << i << ":" << runtimeFunctions[i]->name()->toQString();
229 qDebug() << "root function at index "
230 << (data->indexOfRootFunction != -1
231 ? data->indexOfRootFunction : 0);
232 }
233
234 if (data->indexOfRootFunction != -1)
235 return runtimeFunctions[data->indexOfRootFunction];
236 else
237 return nullptr;
238}
239
240Heap::Object *ExecutableCompilationUnit::templateObjectAt(int index) const
241{
242 Q_ASSERT(index < int(data->templateObjectTableSize));
243 if (!templateObjects.size())
244 templateObjects.resize(data->templateObjectTableSize);
245 Heap::Object *o = templateObjects.at(index);
246 if (o)
247 return o;
248
249 // create the template object
250 Scope scope(engine);
251 const CompiledData::TemplateObject *t = data->templateObjectAt(index);
252 Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size));
253 Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size));
254 ScopedValue s(scope);
255 for (uint i = 0; i < t->size; ++i) {
256 s = runtimeStrings[t->stringIndexAt(i)];
257 a->arraySet(i, s);
258 s = runtimeStrings[t->rawStringIndexAt(i)];
259 raw->arraySet(i, s);
260 }
261
262 ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1);
263 a->defineReadonlyProperty(QStringLiteral("raw"), raw);
264 ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1);
265
266 templateObjects[index] = a->objectValue()->d();
267 return templateObjects.at(index);
268}
269
270void ExecutableCompilationUnit::unlink()
271{
272 if (engine)
273 nextCompilationUnit.remove();
274
275 // Clear the QQmlTypes but not the property caches.
276 // The property caches may still be necessary to resolve further types.
277 qmlType = QQmlType();
278 for (auto &ic : inlineComponentData)
279 ic.qmlType = QQmlType();
280
281 if (runtimeLookups) {
282 for (uint i = 0; i < data->lookupTableSize; ++i)
283 runtimeLookups[i].releasePropertyCache();
284 }
285
286 dependentScripts.clear();
287
288 typeNameCache.reset();
289
290 qDeleteAll(resolvedTypes);
291 resolvedTypes.clear();
292
293 engine = nullptr;
294
295 delete [] runtimeLookups;
296 runtimeLookups = nullptr;
297
298 for (QV4::Function *f : std::as_const(runtimeFunctions))
299 f->destroy();
300 runtimeFunctions.clear();
301
302 free(runtimeStrings);
303 runtimeStrings = nullptr;
304 delete [] runtimeRegularExpressions;
305 runtimeRegularExpressions = nullptr;
306 free(runtimeClasses);
307 runtimeClasses = nullptr;
308}
309
310void ExecutableCompilationUnit::markObjects(QV4::MarkStack *markStack)
311{
312 if (runtimeStrings) {
313 for (uint i = 0, end = totalStringCount(); i < end; ++i)
314 if (runtimeStrings[i])
315 runtimeStrings[i]->mark(markStack);
316 }
317 if (runtimeRegularExpressions) {
318 for (uint i = 0; i < data->regexpTableSize; ++i)
319 Value::fromStaticValue(runtimeRegularExpressions[i]).mark(markStack);
320 }
321 if (runtimeClasses) {
322 for (uint i = 0; i < data->jsClassTableSize; ++i)
323 if (runtimeClasses[i])
324 runtimeClasses[i]->mark(markStack);
325 }
326 for (QV4::Function *f : std::as_const(runtimeFunctions))
327 if (f && f->internalClass)
328 f->internalClass->mark(markStack);
329 for (QV4::Heap::InternalClass *c : std::as_const(runtimeBlocks))
330 if (c)
331 c->mark(markStack);
332
333 for (QV4::Heap::Object *o : std::as_const(templateObjects))
334 if (o)
335 o->mark(markStack);
336
337 if (runtimeLookups) {
338 for (uint i = 0; i < data->lookupTableSize; ++i)
339 runtimeLookups[i].markObjects(markStack);
340 }
341
342 if (auto mod = module())
343 mod->mark(markStack);
344}
345
346IdentifierHash ExecutableCompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
347{
348 IdentifierHash namedObjectCache(engine);
349 const CompiledData::Object *component = objectAt(componentObjectIndex);
350 const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
351 for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
352 const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr);
353 namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->objectId());
354 }
355 Q_ASSERT(!namedObjectCache.isEmpty());
356 return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
357}
358
359template<typename F>
361 const QQmlType &type, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
362 F &&populateIcData)
363{
364 if (type.isInlineComponentType()) {
365 QString icRootName;
366 if (compilationUnit->icRootName) {
367 icRootName = type.elementName();
368 std::swap(*compilationUnit->icRootName, icRootName);
369 } else {
370 compilationUnit->icRootName = std::make_unique<QString>(type.elementName());
371 }
372
373 populateIcData();
374
375 if (icRootName.isEmpty())
376 compilationUnit->icRootName.reset();
377 else
378 std::swap(*compilationUnit->icRootName, icRootName);
379 } else {
380 populateIcData();
381 }
382}
383
384void ExecutableCompilationUnit::finalizeCompositeType(const QQmlType &type)
385{
386 // Add to type registry of composites
387 if (propertyCaches.needsVMEMetaObject(/*root object*/0)) {
388 // qmlType is only valid for types that have references to themselves.
389 if (type.isValid()) {
390 qmlType = type;
391 } else {
393 finalUrl(), this, (unitData()->flags & CompiledData::Unit::IsSingleton)
396 }
397
399 } else {
400 const QV4::CompiledData::Object *obj = objectAt(/*root object*/0);
401 auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex);
402 Q_ASSERT(typeRef);
403 if (const auto compilationUnit = typeRef->compilationUnit())
404 qmlType = compilationUnit->qmlType;
405 else
406 qmlType = typeRef->type();
407 }
408
409 // Collect some data for instantiation later.
410 using namespace icutils;
411 std::vector<QV4::CompiledData::InlineComponent> allICs {};
412 for (int i=0; i != objectCount(); ++i) {
413 const CompiledObject *obj = objectAt(i);
414 for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) {
415 allICs.push_back(*it);
416 }
417 }
418 NodeList nodes;
419 nodes.resize(allICs.size());
420 std::iota(nodes.begin(), nodes.end(), 0);
421 AdjacencyList adjacencyList;
422 adjacencyList.resize(nodes.size());
423 fillAdjacencyListForInlineComponents(this, adjacencyList, nodes, allICs);
424 bool hasCycle = false;
425 auto nodesSorted = topoSort(nodes, adjacencyList, hasCycle);
426 Q_ASSERT(!hasCycle); // would have already been discovered by qqmlpropertycachcecreator
427
428 // We need to first iterate over all inline components, as the containing component might create instances of them
429 // and in that case we need to add its object count
430 for (auto nodeIt = nodesSorted.rbegin(); nodeIt != nodesSorted.rend(); ++nodeIt) {
431 const auto &ic = allICs.at(nodeIt->index());
432 const int lastICRoot = ic.objectIndex;
433 for (int i = ic.objectIndex; i<objectCount(); ++i) {
434 const QV4::CompiledData::Object *obj = objectAt(i);
435 bool leftCurrentInlineComponent
436 = (i != lastICRoot
439 if (leftCurrentInlineComponent)
440 break;
441 const QString lastICRootName = stringAt(ic.nameIndex);
442 inlineComponentData[lastICRootName].totalBindingCount += obj->nBindings;
443
444 if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
445 const auto type = typeRef->type();
446 if (type.isValid() && type.parserStatusCast() != -1)
447 ++inlineComponentData[lastICRootName].totalParserStatusCount;
448
449 ++inlineComponentData[lastICRootName].totalObjectCount;
450 if (const auto compilationUnit = typeRef->compilationUnit()) {
451 // if the type is an inline component type, we have to extract the information from it
452 // This requires that inline components are visited in the correct order
453 processInlinComponentType(type, compilationUnit, [&]() {
454 auto &icData = inlineComponentData[lastICRootName];
455 icData.totalBindingCount += compilationUnit->totalBindingsCount();
456 icData.totalParserStatusCount += compilationUnit->totalParserStatusCount();
457 icData.totalObjectCount += compilationUnit->totalObjectCount();
458 });
459 }
460 }
461 }
462 }
463 int bindingCount = 0;
464 int parserStatusCount = 0;
465 int objectCount = 0;
466 for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
467 const QV4::CompiledData::Object *obj = objectAt(i);
469 continue;
470
471 bindingCount += obj->nBindings;
472 if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
473 const auto type = typeRef->type();
474 if (type.isValid() && type.parserStatusCast() != -1)
475 ++parserStatusCount;
476 ++objectCount;
477 if (const auto compilationUnit = typeRef->compilationUnit()) {
478 processInlinComponentType(type, compilationUnit, [&](){
479 bindingCount += compilationUnit->totalBindingsCount();
480 parserStatusCount += compilationUnit->totalParserStatusCount();
481 objectCount += compilationUnit->totalObjectCount();
482 });
483 }
484 }
485 }
486
487 m_totalBindingsCount = bindingCount;
488 m_totalParserStatusCount = parserStatusCount;
489 m_totalObjectCount = objectCount;
490}
491
492int ExecutableCompilationUnit::totalBindingsCount() const {
493 if (!icRootName)
494 return m_totalBindingsCount;
495 return inlineComponentData[*icRootName].totalBindingCount;
496}
497
498int ExecutableCompilationUnit::totalObjectCount() const {
499 if (!icRootName)
500 return m_totalObjectCount;
501 return inlineComponentData[*icRootName].totalObjectCount;
502}
503
504int ExecutableCompilationUnit::totalParserStatusCount() const {
505 if (!icRootName)
506 return m_totalParserStatusCount;
507 return inlineComponentData[*icRootName].totalParserStatusCount;
508}
509
510bool ExecutableCompilationUnit::verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const
511{
512 if (!dependencyHasher) {
513 for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) {
514 if (data->dependencyMD5Checksum[i] != 0)
515 return false;
516 }
517 return true;
518 }
519 const QByteArray checksum = dependencyHasher();
520 return checksum.size() == sizeof(data->dependencyMD5Checksum)
521 && memcmp(data->dependencyMD5Checksum, checksum.constData(),
522 sizeof(data->dependencyMD5Checksum)) == 0;
523}
524
525QQmlType ExecutableCompilationUnit::qmlTypeForComponent(const QString &inlineComponentName) const
526{
527 if (inlineComponentName.isEmpty())
528 return qmlType;
529 return inlineComponentData[inlineComponentName].qmlType;
530}
531
532QStringList ExecutableCompilationUnit::moduleRequests() const
533{
535 requests.reserve(data->moduleRequestTableSize);
536 for (uint i = 0; i < data->moduleRequestTableSize; ++i)
537 requests << stringAt(data->moduleRequestTable()[i]);
538 return requests;
539}
540
541Heap::Module *ExecutableCompilationUnit::instantiate(ExecutionEngine *engine)
542{
543 if (isESModule() && module())
544 return module();
545
546 if (data->indexOfRootFunction < 0)
547 return nullptr;
548
549 if (!this->engine)
550 linkToEngine(engine);
551
552 Scope scope(engine);
553 Scoped<Module> module(scope, engine->memoryManager->allocate<Module>(engine, this));
554
555 if (isESModule())
556 setModule(module->d());
557
558 for (const QString &request: moduleRequests()) {
559 const QUrl url(request);
560 const auto dependentModuleUnit = engine->loadModule(url, this);
561 if (engine->hasException)
562 return nullptr;
563 if (dependentModuleUnit.compiled)
564 dependentModuleUnit.compiled->instantiate(engine);
565 }
566
567 ScopedString importName(scope);
568
569 const uint importCount = data->importEntryTableSize;
570 if (importCount > 0) {
571 imports = new const StaticValue *[importCount];
572 memset(imports, 0, importCount * sizeof(StaticValue *));
573 }
574 for (uint i = 0; i < importCount; ++i) {
575 const CompiledData::ImportEntry &entry = data->importEntryTable()[i];
576 QUrl url = urlAt(entry.moduleRequest);
577 importName = runtimeStrings[entry.importName];
578
579 const auto module = engine->loadModule(url, this);
580 if (module.compiled) {
581 const Value *valuePtr = module.compiled->resolveExport(importName);
582 if (!valuePtr) {
583 QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
584 referenceErrorMessage += importName->toQString();
585 engine->throwReferenceError(
586 referenceErrorMessage, fileName(),
587 entry.location.line(), entry.location.column());
588 return nullptr;
589 }
590 imports[i] = valuePtr;
591 } else if (Value *value = module.native) {
592 const QString name = importName->toQString();
593 if (value->isNullOrUndefined()) {
595 errorMessage += QStringLiteral(" from ");
597 errorMessage += QStringLiteral(" is null");
599 return nullptr;
600 }
601
602 if (name == QStringLiteral("default")) {
603 imports[i] = value;
604 } else {
606 const auto fragment = engine->moduleForUrl(url, this);
607 if (fragment.native) {
608 imports[i] = fragment.native;
609 } else {
610 Scope scope(this->engine);
611 ScopedObject o(scope, value);
612 if (!o) {
613 QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
614 referenceErrorMessage += name;
615 referenceErrorMessage += QStringLiteral(" because ");
616 referenceErrorMessage += url.toString(QUrl::RemoveFragment);
617 referenceErrorMessage += QStringLiteral(" is not an object");
618 engine->throwReferenceError(
619 referenceErrorMessage, fileName(),
620 entry.location.line(), entry.location.column());
621 return nullptr;
622 }
623
625 const ScopedValue result(scope, o->get(key));
626 imports[i] = engine->registerNativeModule(url, result);
627 }
628 }
629 }
630 }
631
632 const auto throwReferenceError = [&](const CompiledData::ExportEntry &entry, const QString &importName) {
633 QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
634 referenceErrorMessage += importName;
635 engine->throwReferenceError(
636 referenceErrorMessage, fileName(),
637 entry.location.line(), entry.location.column());
638 };
639
640 for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
641 const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
642 auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this);
643 ScopedString importName(scope, runtimeStrings[entry.importName]);
644 if (const auto dependentModuleUnit = dependentModule.compiled) {
645 if (!dependentModuleUnit->resolveExport(importName)) {
646 throwReferenceError(entry, importName->toQString());
647 return nullptr;
648 }
649 } else if (const auto native = dependentModule.native) {
650 ScopedObject o(scope, native);
651 const ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(importName));
652 const ScopedValue result(scope, o->get(key));
653 if (result->isUndefined()) {
654 throwReferenceError(entry, importName->toQString());
655 return nullptr;
656 }
657 }
658 }
659
660 return module->d();
661}
662
663const Value *ExecutableCompilationUnit::resolveExportRecursively(
664 QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
665{
666 if (!module())
667 return nullptr;
668
669 for (const auto &entry: *resolveSet)
670 if (entry.module == this && entry.exportName->isEqualTo(exportName))
672
673 (*resolveSet) << ResolveSetEntry(this, exportName);
674
675 if (exportName->toQString() == QLatin1String("*"))
676 return &module()->self;
677
678 Scope scope(engine);
679
680 if (auto localExport = lookupNameInExportTable(
681 data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) {
682 ScopedString localName(scope, runtimeStrings[localExport->localName]);
683 uint index = module()->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey());
684 if (index == UINT_MAX)
685 return nullptr;
686 if (index >= module()->scope->locals.size)
687 return &(imports[index - module()->scope->locals.size]->asValue<Value>());
688 return &module()->scope->locals[index];
689 }
690
691 if (auto indirectExport = lookupNameInExportTable(
692 data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
693 QUrl request = urlAt(indirectExport->moduleRequest);
694 auto dependentModule = engine->loadModule(request, this);
695 ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
696 if (dependentModule.compiled) {
697 return dependentModule.compiled->resolveExportRecursively(importName, resolveSet);
698 } else if (dependentModule.native) {
699 if (exportName->toQString() == QLatin1String("*"))
700 return dependentModule.native;
701 if (exportName->toQString() == QLatin1String("default"))
702 return nullptr;
703
704 request.setFragment(importName->toQString());
705 const auto fragment = engine->moduleForUrl(request);
706 if (fragment.native)
707 return fragment.native;
708
709 ScopedObject o(scope, dependentModule.native);
710 if (o)
711 return engine->registerNativeModule(request, o->get(importName));
712
713 return nullptr;
714 } else {
715 return nullptr;
716 }
717 }
718
719 if (exportName->toQString() == QLatin1String("default"))
720 return nullptr;
721
722 const Value *starResolution = nullptr;
723
724 for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
725 const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
726 QUrl request = urlAt(entry.moduleRequest);
727 auto dependentModule = engine->loadModule(request, this);
728 const Value *resolution = nullptr;
729 if (dependentModule.compiled) {
730 resolution = dependentModule.compiled->resolveExportRecursively(
731 exportName, resolveSet);
732 } else if (dependentModule.native) {
733 if (exportName->toQString() == QLatin1String("*")) {
734 resolution = dependentModule.native;
735 } else if (exportName->toQString() != QLatin1String("default")) {
736 request.setFragment(exportName->toQString());
737 const auto fragment = engine->moduleForUrl(request);
738 if (fragment.native) {
739 resolution = fragment.native;
740 } else {
741 ScopedObject o(scope, dependentModule.native);
742 if (o)
743 resolution = engine->registerNativeModule(request, o->get(exportName));
744 }
745 }
746 }
747
748 // ### handle ambiguous
749 if (resolution) {
750 if (!starResolution) {
751 starResolution = resolution;
752 continue;
753 }
754 if (resolution != starResolution)
755 return nullptr;
756 }
757 }
758
759 return starResolution;
760}
761
762const CompiledData::ExportEntry *ExecutableCompilationUnit::lookupNameInExportTable(
763 const CompiledData::ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
764{
765 const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
766 auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
767 return stringAt(lhs.exportName) < name->toQString();
768 });
769 if (matchingExport == lastExportEntry || stringAt(matchingExport->exportName) != name->toQString())
770 return nullptr;
771 return matchingExport;
772}
773
774void ExecutableCompilationUnit::getExportedNamesRecursively(
776 bool includeDefaultExport) const
777{
778 if (exportNameSet->contains(this))
779 return;
780 exportNameSet->append(this);
781
782 const auto append = [names, includeDefaultExport](const QString &name) {
783 if (!includeDefaultExport && name == QLatin1String("default"))
784 return;
785 names->append(name);
786 };
787
788 for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
789 const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
790 append(stringAt(entry.exportName));
791 }
792
793 for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
794 const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
795 append(stringAt(entry.exportName));
796 }
797
798 for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
799 const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
800 auto dependentModule = engine->loadModule(urlAt(entry.moduleRequest), this);
801 if (dependentModule.compiled) {
802 dependentModule.compiled->getExportedNamesRecursively(
803 names, exportNameSet, /*includeDefaultExport*/false);
804 } else if (dependentModule.native) {
805 Scope scope(engine);
806 ScopedObject o(scope, dependentModule.native);
807 ObjectIterator iterator(scope, o, ObjectIterator::EnumerableOnly);
808 while (true) {
809 ScopedValue val(scope, iterator.nextPropertyNameAsString());
810 if (val->isNull())
811 break;
812 append(val->toQString());
813 }
814 }
815 }
816}
817
818void ExecutableCompilationUnit::evaluate()
819{
820 QV4::Scope scope(engine);
821 QV4::Scoped<Module> mod(scope, module());
822 mod->evaluate();
823}
824
825void ExecutableCompilationUnit::evaluateModuleRequests()
826{
827 for (const QString &request: moduleRequests()) {
828 auto dependentModule = engine->loadModule(QUrl(request), this);
829 if (dependentModule.native)
830 continue;
831
832 if (engine->hasException)
833 return;
834
835 Q_ASSERT(dependentModule.compiled);
836 dependentModule.compiled->evaluate();
837 if (engine->hasException)
838 return;
839 }
840}
841
842bool ExecutableCompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
843{
845 *errorString = QStringLiteral("File has to be a local file.");
846 return false;
847 }
848
849 const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
850 auto cacheFile = std::make_unique<CompilationUnitMapper>();
851
852 const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
853 for (const QString &cachePath : cachePaths) {
854 CompiledData::Unit *mappedUnit = cacheFile->get(cachePath, sourceTimeStamp, errorString);
855 if (!mappedUnit)
856 continue;
857
858 const CompiledData::Unit * const oldDataPtr
860 : nullptr;
861 const CompiledData::Unit *oldData = data;
862 auto dataPtrRevert = qScopeGuard([this, oldData](){
863 setUnitData(oldData);
864 });
865 setUnitData(mappedUnit);
866
867 if (data->sourceFileIndex != 0) {
868 if (data->sourceFileIndex >= data->stringTableSize + dynamicStrings.size()) {
869 *errorString = QStringLiteral("QML source file index is invalid.");
870 continue;
871 }
872 if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
873 *errorString = QStringLiteral("QML source file has moved to a different location.");
874 continue;
875 }
876 }
877
878 dataPtrRevert.dismiss();
879 free(const_cast<CompiledData::Unit*>(oldDataPtr));
880 backingFile = std::move(cacheFile);
881 return true;
882 }
883
884 return false;
885}
886
887bool ExecutableCompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
888{
889 if (data->sourceTimeStamp == 0) {
890 *errorString = QStringLiteral("Missing time stamp for source file");
891 return false;
892 }
893
894 if (!QQmlFile::isLocalFile(unitUrl)) {
895 *errorString = QStringLiteral("File has to be a local file.");
896 return false;
897 }
898
899 return CompiledData::SaveableUnitPointer(unitData()).saveToDisk<char>(
900 [&unitUrl, errorString](const char *data, quint32 size) {
901 const QString cachePath = localCacheFilePath(unitUrl);
902 if (CompiledData::SaveableUnitPointer::writeDataToFile(
903 cachePath, data, size, errorString)) {
904 CompilationUnitMapper::invalidate(cachePath);
905 return true;
906 }
907
908 return false;
909 });
910}
911
917bool ResolvedTypeReferenceMap::addToHash(
919{
920 std::vector<int> keys (size());
921 int i = 0;
922 for (auto it = constBegin(), end = constEnd(); it != end; ++it) {
923 keys[i] = it.key();
924 ++i;
925 }
926 std::sort(keys.begin(), keys.end());
927 for (int key: keys) {
928 if (!this->operator[](key)->addToHash(hash, checksums))
929 return false;
930 }
931
932 return true;
933}
934
935QString ExecutableCompilationUnit::bindingValueAsString(const CompiledData::Binding *binding) const
936{
937#if QT_CONFIG(translation)
938 using namespace CompiledData;
939 bool byId = false;
940 switch (binding->type()) {
941 case Binding::Type_TranslationById:
942 byId = true;
944 case Binding::Type_Translation: {
945 return translateFrom({ binding->value.translationDataIndex, byId });
946 }
947 default:
948 break;
949 }
950#endif
951 return CompilationUnit::bindingValueAsString(binding);
952}
953
954QString ExecutableCompilationUnit::translateFrom(TranslationDataIndex index) const
955{
956#if !QT_CONFIG(translation)
957 return QString();
958#else
959 const CompiledData::TranslationData &translation = data->translations()[index.index];
960
961 if (index.byId) {
962 QByteArray id = stringAt(translation.stringIndex).toUtf8();
963 return qtTrId(id.constData(), translation.number);
964 }
965
966 const auto fileContext = [this]() {
967 // This code must match that in the qsTr() implementation
968 const QString &path = fileName();
969 int lastSlash = path.lastIndexOf(QLatin1Char('/'));
970
971 QStringView context = (lastSlash > -1)
972 ? QStringView{ path }.mid(lastSlash + 1, path.size() - lastSlash - 5)
973 : QStringView();
974 return context.toUtf8();
975 };
976
977 QByteArray context = stringAt(translation.contextIndex).toUtf8();
978 QByteArray comment = stringAt(translation.commentIndex).toUtf8();
979 QByteArray text = stringAt(translation.stringIndex).toUtf8();
981 context.isEmpty() ? fileContext() : context, text, comment, translation.number);
982#endif
983}
984
985bool ExecutableCompilationUnit::verifyHeader(
986 const CompiledData::Unit *unit, QDateTime expectedSourceTimeStamp, QString *errorString)
987{
988 if (strncmp(unit->magic, CompiledData::magic_str, sizeof(unit->magic))) {
989 *errorString = QStringLiteral("Magic bytes in the header do not match");
990 return false;
991 }
992
994 *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2")
995 .arg(unit->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16);
996 return false;
997 }
998
999 if (unit->qtVersion != quint32(QT_VERSION)) {
1000 *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2")
1001 .arg(unit->qtVersion, 0, 16).arg(QT_VERSION, 0, 16);
1002 return false;
1003 }
1004
1005 if (unit->sourceTimeStamp) {
1006 // Files from the resource system do not have any time stamps, so fall back to the application
1007 // executable.
1008 if (!expectedSourceTimeStamp.isValid())
1009 expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified();
1010
1011 if (expectedSourceTimeStamp.isValid()
1012 && expectedSourceTimeStamp.toMSecsSinceEpoch() != unit->sourceTimeStamp) {
1013 *errorString = QStringLiteral("QML source file has a different time stamp than cached file.");
1014 return false;
1015 }
1016 }
1017
1018#if defined(QML_COMPILE_HASH) && defined(QML_COMPILE_HASH_LENGTH) && QML_COMPILE_HASH_LENGTH > 0
1019 if (qstrncmp(qml_compile_hash, unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH) != 0) {
1020 *errorString = QStringLiteral("QML compile hashes don't match. Found %1 expected %2")
1022 QByteArray(unit->libraryVersionHash, QML_COMPILE_HASH_LENGTH)
1025 QByteArray(qml_compile_hash, QML_COMPILE_HASH_LENGTH)
1026 .toPercentEncoding()));
1027 return false;
1028 }
1029#else
1030#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
1031#endif
1032 return true;
1033}
1034
1035} // namespace QV4
1036
DarwinBluetooth::RequestQueue requests
\inmodule QtCore
Definition qbytearray.h:57
QByteArray toPercentEncoding(const QByteArray &exclude=QByteArray(), const QByteArray &include=QByteArray(), char percent='%') const
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
QByteArray toHex(char separator='\0') const
Returns a hex encoded copy of the byte array.
static QString applicationFilePath()
Returns the file path of the application executable.
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
void addData(QByteArrayView data) noexcept
Adds the characters in bytes to the cryptographic hash.
QByteArray result() const
Returns the final hash value.
\inmodule QtCore\reentrant
Definition qdatetime.h:257
qint64 toMSecsSinceEpoch() const
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
\inmodule QtCore
static QDir root()
Returns the root directory.
Definition qdir.h:221
bool mkpath(const QString &dirPath) const
Creates the directory path dirPath.
Definition qdir.cpp:1579
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QDateTime lastModified() const
Returns the date and time when the file was last modified.
Definition qfileinfo.h:156
QString completeSuffix() const
Returns the complete suffix (extension) of the file.
\inmodule QtCore
Definition qhash.h:818
void throwError(const QString &message)
Throws a run-time error (exception) with the given message.
Definition qlist.h:74
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
static bool isLocalFile(const QString &url)
Returns true if url is a local file that can be opened with QFile.
Definition qqmlfile.cpp:549
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to QFile.
Definition qqmlfile.cpp:643
static void registerInternalCompositeType(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit)
static QQmlType findCompositeType(const QUrl &url, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, CompositeTypeLookupMode mode=NonSingleton)
void reset(T *t=nullptr)
static QString writableLocation(StandardLocation type)
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QByteArray toUtf8() const &
Definition qstring.h:563
\inmodule QtCore
Definition qurl.h:94
void setFragment(const QString &fragment, ParsingMode mode=TolerantMode)
Sets the fragment of the URL to fragment.
Definition qurl.cpp:2645
@ RemoveFragment
Definition qurl.h:112
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
bool saveToDisk(const std::function< bool(const Char *, quint32)> &writer) const
#define this
Definition dialogs.cpp:9
QHash< int, QWidget * > hash
[35multi]
QString text
list append(new Employee("Blackpool", "Stephen"))
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
typename C::iterator iterator
Combined button and popup list for selecting options.
std::function< QByteArray()> DependentTypesHasher
\qmltype Particle \inqmlmodule QtQuick.Particles
static void dumpConstantTable(const StaticValue *constants, uint count)
quint64 ReturnedValue
Scoped< Object > ScopedObject
@ Attr_NotConfigurable
@ Attr_Data
@ Attr_Accessor
void processInlinComponentType(const QQmlType &type, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, F &&populateIcData)
QTextStream & right(QTextStream &stream)
Calls QTextStream::setFieldAlignment(QTextStream::AlignRight) on stream and returns stream.
std::vector< std::vector< Node * > > AdjacencyList
static void * context
int qstrncmp(const char *str1, const char *str2, size_t len)
#define Q_FALLTHROUGH()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static quint32 checksum(const QByteArray &table)
#define qDebug
[1]
Definition qlogging.h:160
GLsizei const GLfloat * v
[13]
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLenum type
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint name
GLhandleARB obj
[2]
const GLubyte * c
GLuint GLfloat * val
GLuint GLuint * names
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
static qreal component(const QPointF &point, unsigned int i)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
QTextStreamManipulator qSetFieldWidth(int width)
Q_CORE_EXPORT QString qtTrId(const char *id, int n=-1)
#define QT_VERSION
unsigned int quint32
Definition qtypes.h:45
unsigned int uint
Definition qtypes.h:29
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3503
#define QV4_DATA_STRUCTURE_VERSION
if(qFloatDistance(a, b)<(1<< 7))
[0]
QStringList keys
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QNetworkRequest request(url)
char * toString(const MyType &t)
[31]
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:17
bool contains(const AT &t) const noexcept
Definition qlist.h:44
void(* functionPtr)(const AOTCompiledContext *context, void *resultPtr, void **arguments)
union QV4::CompiledData::Binding::@543 value
const quint32_le * localsTable() const
char libraryVersionHash[QmlCompileHashSpace]
IdentifierTable * identifierTable
static Function * create(ExecutionEngine *engine, ExecutableCompilationUnit *unit, const CompiledData::Function *function, const QQmlPrivate::AOTCompiledFunction *aotFunction)
PropertyKey asPropertyKey(const Heap::String *str)
ReturnedValue(* globalGetter)(Lookup *l, ExecutionEngine *engine)
Definition qv4lookup_p.h:37
ReturnedValue(* qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject)
Definition qv4lookup_p.h:38
ReturnedValue(* getter)(Lookup *l, ExecutionEngine *engine, const Value &object)
Definition qv4lookup_p.h:36
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object)
static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
bool(* setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
Definition qv4lookup_p.h:39
static Heap::RegExp * create(ExecutionEngine *engine, const QString &pattern, uint flags=CompiledData::RegExp::RegExp_NoFlags)
ExecutionEngine * engine
QString toQString() const