diff options
Diffstat (limited to 'src/qml/compiler/qv4compileddata.cpp')
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 409 |
1 files changed, 398 insertions, 11 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a63f35152a..e815c41a86 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -47,12 +47,30 @@ #include <private/qv4lookup_p.h> #include <private/qv4regexpobject_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmltypeloader_p.h> +#include <private/qqmlengine_p.h> +#include "qv4compilationunitmapper_p.h" +#include <QQmlPropertyMap> +#include <QDateTime> +#include <QSaveFile> +#include <QFile> +#include <QFileInfo> +#include <QScopedValueRollback> +#include <QStandardPaths> +#include <QDir> #endif #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> +#include <QCryptographicHash> #include <algorithm> +#if defined(QT_BUILD_INTERNAL) +#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) +#include <dlfcn.h> +#endif +#endif + QT_BEGIN_NAMESPACE namespace QV4 { @@ -67,11 +85,20 @@ CompilationUnit::CompilationUnit() , runtimeLookups(0) , runtimeRegularExpressions(0) , runtimeClasses(0) + , totalBindingsCount(0) + , totalParserStatusCount(0) + , totalObjectCount(0) + , metaTypeId(-1) + , listMetaTypeId(-1) + , isRegisteredWithEngine(false) {} CompilationUnit::~CompilationUnit() { unlink(); + if (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) + free(const_cast<Unit *>(data)); + data = 0; } QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) @@ -109,7 +136,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) for (uint i = 0; i < data->lookupTableSize; ++i) { QV4::Lookup *l = runtimeLookups + i; - Lookup::Type type = Lookup::Type(compiledLookups[i].type_and_flags); + Lookup::Type type = Lookup::Type(uint(compiledLookups[i].type_and_flags)); if (type == CompiledData::Lookup::Type_Getter) l->getter = QV4::Lookup::getterGeneric; else if (type == CompiledData::Lookup::Type_Setter) @@ -144,14 +171,18 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) } } - linkBackendToEngine(engine); - -#if 0 - runtimeFunctionsSortedByAddress.resize(runtimeFunctions.size()); - memcpy(runtimeFunctionsSortedByAddress.data(), runtimeFunctions.data(), runtimeFunctions.size() * sizeof(QV4::Function*)); - std::sort(runtimeFunctionsSortedByAddress.begin(), runtimeFunctionsSortedByAddress.end(), functionSortHelper); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + Value *bigEndianConstants = new Value[data->constantTableSize]; + const LEUInt64 *littleEndianConstants = data->constants(); + for (uint i = 0; i < data->constantTableSize; ++i) + bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]); + constants = bigEndianConstants; +#else + constants = reinterpret_cast<const Value*>(data->constants()); #endif + linkBackendToEngine(engine); + if (data->indexOfRootFunction != -1) return runtimeFunctions[data->indexOfRootFunction]; else @@ -162,10 +193,26 @@ void CompilationUnit::unlink() { if (engine) engine->compilationUnits.erase(engine->compilationUnits.find(this)); + + if (isRegisteredWithEngine) { + Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject)); + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(propertyCaches.at(data->indexOfRootObject)->engine); + qmlEngine->unregisterInternalCompositeType(this); + isRegisteredWithEngine = false; + } + + propertyCaches.clear(); + + for (int ii = 0; ii < dependentScripts.count(); ++ii) + dependentScripts.at(ii)->release(); + dependentScripts.clear(); + + importCache = nullptr; + + qDeleteAll(resolvedTypes); + resolvedTypes.clear(); + engine = 0; - if (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) - free(data); - data = 0; free(runtimeStrings); runtimeStrings = 0; delete [] runtimeLookups; @@ -176,6 +223,9 @@ void CompilationUnit::unlink() runtimeClasses = 0; qDeleteAll(runtimeFunctions); runtimeFunctions.clear(); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + delete [] constants; +#endif } void CompilationUnit::markObjects(QV4::ExecutionEngine *e) @@ -189,6 +239,219 @@ void CompilationUnit::markObjects(QV4::ExecutionEngine *e) } } +void CompilationUnit::destroy() +{ + QQmlEngine *qmlEngine = 0; + if (engine && engine->v8Engine) + qmlEngine = engine->v8Engine->engine(); + if (qmlEngine) + QQmlEnginePrivate::deleteInEngineThread(qmlEngine, this); + else + delete this; +} + +IdentifierHash<int> CompilationUnit::namedObjectsPerComponent(int componentObjectIndex) +{ + auto it = namedObjectsPerComponentCache.find(componentObjectIndex); + if (it == namedObjectsPerComponentCache.end()) { + IdentifierHash<int> namedObjectCache(engine); + const CompiledData::Object *component = data->objectAt(componentObjectIndex); + const LEUInt32 *namedObjectIndexPtr = component->namedObjectsInComponentTable(); + for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) { + const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr); + namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id); + } + it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache); + } + return *it; +} + +void CompilationUnit::finalize(QQmlEnginePrivate *engine) +{ + // Add to type registry of composites + if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject)) + engine->registerInternalCompositeType(this); + else { + const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject); + auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); + Q_ASSERT(typeRef); + if (typeRef->compilationUnit) { + metaTypeId = typeRef->compilationUnit->metaTypeId; + listMetaTypeId = typeRef->compilationUnit->listMetaTypeId; + } else { + metaTypeId = typeRef->type->typeId(); + listMetaTypeId = typeRef->type->qListTypeId(); + } + } + + // Collect some data for instantiation later. + int bindingCount = 0; + int parserStatusCount = 0; + int objectCount = 0; + for (quint32 i = 0; i < data->nObjects; ++i) { + const QV4::CompiledData::Object *obj = data->objectAt(i); + bindingCount += obj->nBindings; + if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { + if (QQmlType *qmlType = typeRef->type) { + if (qmlType->parserStatusCast() != -1) + ++parserStatusCount; + } + ++objectCount; + if (typeRef->compilationUnit) { + bindingCount += typeRef->compilationUnit->totalBindingsCount; + parserStatusCount += typeRef->compilationUnit->totalParserStatusCount; + objectCount += typeRef->compilationUnit->totalObjectCount; + } + } + } + + totalBindingsCount = bindingCount; + totalParserStatusCount = parserStatusCount; + totalObjectCount = objectCount; +} + +bool CompilationUnit::verifyChecksum(QQmlEngine *engine, + const ResolvedTypeReferenceMap &dependentTypes) const +{ + if (dependentTypes.isEmpty()) { + for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { + if (data->dependencyMD5Checksum[i] != 0) + return false; + } + return true; + } + QCryptographicHash hash(QCryptographicHash::Md5); + if (!dependentTypes.addToHash(&hash, engine)) + return false; + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum)); + return memcmp(data->dependencyMD5Checksum, checksum.constData(), + sizeof(data->dependencyMD5Checksum)) == 0; +} + +static QString cacheFilePath(const QUrl &url) +{ + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString localCachePath = localSourcePath + QLatin1Char('c'); + if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + return localCachePath; + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); +} + +bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +{ + errorString->clear(); + + if (data->sourceTimeStamp == 0) { + *errorString = QStringLiteral("Missing time stamp for source file"); + return false; + } + + if (!QQmlFile::isLocalFile(unitUrl)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + // Foo.qml -> Foo.qmlc + QSaveFile cacheFile(cacheFilePath(unitUrl)); + if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + *errorString = cacheFile.errorString(); + return false; + } + + QByteArray modifiedUnit; + modifiedUnit.resize(data->unitSize); + memcpy(modifiedUnit.data(), data, data->unitSize); + const char *dataPtr = modifiedUnit.data(); + Unit *unitPtr; + memcpy(&unitPtr, &dataPtr, sizeof(unitPtr)); + unitPtr->flags |= Unit::StaticData; + + prepareCodeOffsetsForDiskStorage(unitPtr); + + qint64 headerWritten = cacheFile.write(modifiedUnit); + if (headerWritten != modifiedUnit.size()) { + *errorString = cacheFile.errorString(); + return false; + } + + if (!saveCodeToDisk(&cacheFile, unitPtr, errorString)) + return false; + + if (!cacheFile.commit()) { + *errorString = cacheFile.errorString(); + return false; + } + + return true; +} + +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +{ + if (!QQmlFile::isLocalFile(url)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = url.toLocalFile(); + QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); + + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); + if (!mappedUnit) + return false; + + const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; + QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + + { + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); + return false; + } + } + + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + if (foundCodeGenerator != expectedCodeGenerator) { + *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); + return false; + } + } + + if (!memoryMapCode(errorString)) + return false; + + dataPtrChange.commit(); + free(const_cast<Unit*>(oldDataPtr)); + backingFile.reset(cacheFile.take()); + return true; +} + +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) +{ + Q_UNUSED(unit); +} + +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) +{ + Q_UNUSED(device); + Q_UNUSED(unit); + *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); + return false; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; +} #endif // V4_BOOTSTRAP Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) @@ -205,7 +468,7 @@ QString Binding::valueAsString(const Unit *unit) const case Type_Boolean: return value.b ? QStringLiteral("true") : QStringLiteral("false"); case Type_Number: - return QString::number(value.d); + return QString::number(valueAsNumber()); case Type_Invalid: return QString(); #ifdef QT_NO_TRANSLATION @@ -287,6 +550,130 @@ QString Binding::valueAsScriptString(const Unit *unit) const return valueAsString(unit); } +#ifndef V4_BOOTSTRAP +/*! +Returns the property cache, if one alread exists. The cache is not referenced. +*/ +QQmlPropertyCache *ResolvedTypeReference::propertyCache() const +{ + if (type) + return typePropertyCache; + else + return compilationUnit->rootPropertyCache(); +} + +/*! +Returns the property cache, creating one if it doesn't already exist. The cache is not referenced. +*/ +QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine) +{ + if (typePropertyCache) { + return typePropertyCache; + } else if (type) { + typePropertyCache = QQmlEnginePrivate::get(engine)->cache(type->metaObject()); + return typePropertyCache; + } else { + return compilationUnit->rootPropertyCache(); + } +} + +bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engine) +{ + if (type) { + bool ok = false; + hash->addData(createPropertyCache(engine)->checksum(&ok)); + return ok; + } + hash->addData(compilationUnit->data->md5Checksum, sizeof(compilationUnit->data->md5Checksum)); + return true; +} + +template <typename T> +bool qtTypeInherits(const QMetaObject *mo) { + while (mo) { + if (mo == &T::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +void ResolvedTypeReference::doDynamicTypeCheck() +{ + const QMetaObject *mo = 0; + if (typePropertyCache) + mo = typePropertyCache->firstCppMetaObject(); + else if (type) + mo = type->metaObject(); + else if (compilationUnit) + mo = compilationUnit->rootPropertyCache()->firstCppMetaObject(); + isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); +} + +#if defined(QT_BUILD_INTERNAL) + +static QByteArray ownLibraryChecksum() +{ + static QByteArray libraryChecksum; + static bool checksumInitialized = false; + if (checksumInitialized) + return libraryChecksum; + checksumInitialized = true; +#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) + Dl_info libInfo; + if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { + QFile library(QFile::decodeName(libInfo.dli_fname)); + if (library.open(QIODevice::ReadOnly)) { + QCryptographicHash hash(QCryptographicHash::Sha1); + hash.addData(&library); + libraryChecksum = hash.result(); + } + } +#else + // Not implemented. +#endif + return libraryChecksum; +} + +#endif + +bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const +{ + for (auto it = constBegin(), end = constEnd(); it != end; ++it) { + if (!it.value()->addToHash(hash, engine)) + return false; + } + + // This is a bit of a hack to make development easier. When hacking on the code generator + // the cache files may end up being re-used. To avoid that we also add the checksum of + // the QtQml library. +#if defined(QT_BUILD_INTERNAL) + hash->addData(ownLibraryChecksum()); +#endif + + return true; +} + +#endif + +void Unit::generateChecksum() +{ +#ifndef V4_BOOTSTRAP + QCryptographicHash hash(QCryptographicHash::Md5); + + const int checksummableDataOffset = qOffsetOf(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum); + + const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset; + hash.addData(dataPtr, unitSize - checksummableDataOffset); + + QByteArray checksum = hash.result(); + Q_ASSERT(checksum.size() == sizeof(md5Checksum)); + memcpy(md5Checksum, checksum.constData(), sizeof(md5Checksum)); +#else + memset(md5Checksum, 0, sizeof(md5Checksum)); +#endif +} + } } |