diff options
Diffstat (limited to 'src/qml/compiler/qv4compileddata.cpp')
-rw-r--r-- | src/qml/compiler/qv4compileddata.cpp | 236 |
1 files changed, 167 insertions, 69 deletions
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6aac111897..8f8d374e24 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -52,7 +52,6 @@ #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> -#include <QSaveFile> #include <QFile> #include <QFileInfo> #include <QScopedValueRollback> @@ -62,6 +61,7 @@ #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> #include <QCryptographicHash> +#include <QSaveFile> #include <algorithm> @@ -77,6 +77,27 @@ namespace QV4 { namespace CompiledData { +#ifdef V4_BOOTSTRAP +static QString cacheFilePath(const QString &localSourcePath) +{ + const QString localCachePath = localSourcePath + QLatin1Char('c'); + return localCachePath; +} +#else +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(); +} +#endif + #ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() : data(0) @@ -207,7 +228,7 @@ void CompilationUnit::unlink() dependentScripts.at(ii)->release(); dependentScripts.clear(); - importCache = nullptr; + typeNameCache = nullptr; qDeleteAll(resolvedTypes); resolvedTypes.clear(); @@ -329,20 +350,68 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } -static QString cacheFilePath(const QUrl &url) +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { - 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(); + if (!QQmlFile::isLocalFile(url)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); + 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); + + if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + *errorString = QStringLiteral("QML source file has moved to a different location."); + return false; + } + + { + 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; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; } +#endif // V4_BOOTSTRAP + +#if defined(V4_BOOTSTRAP) +bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString) +#else bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +#endif { errorString->clear(); @@ -351,10 +420,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return false; } +#if !defined(V4_BOOTSTRAP) if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } +#endif // Foo.qml -> Foo.qmlc QSaveFile cacheFile(cacheFilePath(unitUrl)); @@ -390,78 +461,105 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; } -bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) { - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } + Q_UNUSED(unit); +} - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); +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; +} - CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); - if (!mappedUnit) - return false; +Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) +{ + if (!irDocument->javaScriptCompilationUnit->data) + return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit; + QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data); - if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - return false; - } + QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable; - { - 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; + // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example) + // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize + // the string table. + QVector<quint32> changedSignals; + QVector<QQmlJS::AST::FormalParameterList*> changedSignalParameters; + for (QmlIR::Object *o: qAsConst(irDocument->objects)) { + for (QmlIR::Binding *binding = o->firstBinding(); binding; binding = binding->next) { + if (!(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)) + continue; + + quint32 functionIndex = binding->value.compiledScriptIndex; + QmlIR::CompiledFunctionOrExpression *foe = o->functionsAndExpressions->slowAt(functionIndex); + if (!foe) + continue; + + // save absolute index + changedSignals << o->runtimeFunctionIndices.at(functionIndex); + + Q_ASSERT(foe->node); + Q_ASSERT(QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)); + + QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals; + changedSignalParameters << parameters; + + for (; parameters; parameters = parameters->next) + stringTable.registerString(parameters->name.toString()); } } - { - 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; + QVector<quint32> signalParameterNameTable; + quint32 signalParameterNameTableOffset = jsUnit->unitSize; + + // Update signal signatures + if (!changedSignals.isEmpty()) { + if (jsUnit == compilationUnit->data) { + char *unitCopy = (char*)malloc(jsUnit->unitSize); + memcpy(unitCopy, jsUnit, jsUnit->unitSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy); } - } - if (!memoryMapCode(errorString)) - return false; + for (int i = 0; i < changedSignals.count(); ++i) { + const uint functionIndex = changedSignals.at(i); + // The data is now read-write due to the copy above, so the const_cast is ok. + QV4::CompiledData::Function *function = const_cast<QV4::CompiledData::Function *>(jsUnit->functionAt(functionIndex)); + Q_ASSERT(function->nFormals == quint32(0)); - dataPtrChange.commit(); - free(const_cast<Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; -} + function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex]; -void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) -{ - Q_UNUSED(unit); -} + for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i); + parameters; parameters = parameters->next) { + signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString())); + function->nFormals = function->nFormals + 1; + } -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; -} + // Hack to ensure an activation is created. + function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval; -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - *errorString = QStringLiteral("Missing code mapping backend"); - return false; -} -#endif // V4_BOOTSTRAP + signalParameterNameTableOffset += function->nFormals * sizeof(quint32); + } + } -Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) -{ - return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); + if (!signalParameterNameTable.isEmpty()) { + Q_ASSERT(jsUnit != compilationUnit->data); + const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32); + uint newSize = jsUnit->unitSize + signalParameterTableSize; + const uint oldSize = jsUnit->unitSize; + char *unitWithSignalParameters = (char*)realloc(jsUnit, newSize); + memcpy(unitWithSignalParameters + oldSize, signalParameterNameTable.constData(), signalParameterTableSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitWithSignalParameters); + jsUnit->unitSize = newSize; + } + + if (jsUnit != compilationUnit->data) + jsUnit->flags &= ~QV4::CompiledData::Unit::StaticData; + + return jsUnit; } QString Binding::valueAsString(const Unit *unit) const @@ -624,7 +722,7 @@ static QByteArray ownLibraryChecksum() if (checksumInitialized) return libraryChecksum; checksumInitialized = true; -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) +#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) && !defined(Q_OS_INTEGRITY) Dl_info libInfo; if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); |