| 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 | 
| 74 | const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH; | 
| 75 | static_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 |  | 
| 81 | QT_BEGIN_NAMESPACE | 
| 82 |  | 
| 83 | namespace QV4 { | 
| 84 |  | 
| 85 | ExecutableCompilationUnit::ExecutableCompilationUnit() = default; | 
| 86 |  | 
| 87 | ExecutableCompilationUnit::ExecutableCompilationUnit( | 
| 88 |         CompiledData::CompilationUnit &&compilationUnit) | 
| 89 |     : CompiledData::CompilationUnit(std::move(compilationUnit)) | 
| 90 | {} | 
| 91 |  | 
| 92 | ExecutableCompilationUnit::~ExecutableCompilationUnit() | 
| 93 | { | 
| 94 |     unlink(); | 
| 95 | } | 
| 96 |  | 
| 97 | QString 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 |  | 
| 112 | static 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 |  | 
| 127 | static 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 |  | 
| 137 | QV4::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 |  | 
| 253 | Heap::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 |  | 
| 283 | void 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 |  | 
| 328 | void 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 |  | 
| 364 | IdentifierHash 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 |  | 
| 376 | void 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 |  | 
| 486 | int ExecutableCompilationUnit::totalBindingsCount() const { | 
| 487 |     if (icRoot == -1) | 
| 488 |         return m_totalBindingsCount; | 
| 489 |     return inlineComponentData[icRoot].totalBindingCount; | 
| 490 | } | 
| 491 |  | 
| 492 | int ExecutableCompilationUnit::totalObjectCount() const { | 
| 493 |     if (icRoot == -1) | 
| 494 |         return m_totalObjectCount; | 
| 495 |     return inlineComponentData[icRoot].totalObjectCount; | 
| 496 | } | 
| 497 |  | 
| 498 | int ExecutableCompilationUnit::totalParserStatusCount() const { | 
| 499 |     if (icRoot == -1) | 
| 500 |         return m_totalParserStatusCount; | 
| 501 |     return inlineComponentData[icRoot].totalParserStatusCount; | 
| 502 | } | 
| 503 |  | 
| 504 | bool 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 |  | 
| 519 | CompositeMetaTypeIds ExecutableCompilationUnit::typeIdsForComponent(int objectid) const | 
| 520 | { | 
| 521 |     if (objectid == 0) | 
| 522 |         return {metaTypeId, listMetaTypeId}; | 
| 523 |     return inlineComponentData[objectid].typeIds; | 
| 524 | } | 
| 525 |  | 
| 526 | QStringList 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 |  | 
| 535 | Heap::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 |  | 
| 598 | const 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 |  | 
| 662 | const 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 |  | 
| 674 | void 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 |  | 
| 707 | void ExecutableCompilationUnit::evaluate() | 
| 708 | { | 
| 709 |     QV4::Scope scope(engine); | 
| 710 |     QV4::Scoped<Module> mod(scope, module()); | 
| 711 |     mod->evaluate(); | 
| 712 | } | 
| 713 |  | 
| 714 | void 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 |  | 
| 726 | bool 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 |  | 
| 766 | bool 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 | /*! | 
| 786 | Returns the property cache, if one alread exists.  The cache is not referenced. | 
| 787 | */ | 
| 788 | QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const | 
| 789 | { | 
| 790 |     if (type.isValid()) | 
| 791 |         return typePropertyCache; | 
| 792 |     else | 
| 793 |         return m_compilationUnit->rootPropertyCache(); | 
| 794 | } | 
| 795 |  | 
| 796 | /*! | 
| 797 | Returns the property cache, creating one if it doesn't already exist.  The cache is not referenced. | 
| 798 | */ | 
| 799 | QQmlRefPointer<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 |  | 
| 812 | bool 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 |  | 
| 826 | template <typename T> | 
| 827 | bool 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 |  | 
| 836 | void 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 |  | 
| 848 | bool 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 |  | 
| 858 | QString 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  = 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 |  | 
| 906 | QString 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 |  | 
| 914 | bool ExecutableCompilationUnit::( | 
| 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 |  | 
| 960 | QT_END_NAMESPACE | 
| 961 |  |