Commit 7fb87fbb authored by Fawzi Mohamed's avatar Fawzi Mohamed

qmljs: fingerprints for documents, libraries and FakeMetaObjects

Change-Id: Ib9c9b86fbed19539dc42696292bdb3b93dd1b575
Reviewed-by: default avatarThomas Hartmann <Thomas.Hartmann@digia.com>
parent e1b44e87
......@@ -30,6 +30,7 @@
#include "componentversion.h"
#include <QString>
#include <QCryptographicHash>
#include <limits>
......@@ -77,7 +78,13 @@ bool ComponentVersion::isValid() const
QString ComponentVersion::toString() const
{
return QString::fromLatin1("%1.%2").arg(QString::number(_major),
QString::number(_minor));
QString::number(_minor));
}
void ComponentVersion::addToHash(QCryptographicHash &hash) const
{
hash.addData(reinterpret_cast<const char *>(&_major), sizeof(_major));
hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
}
namespace LanguageUtils {
......
......@@ -32,6 +32,10 @@
#include "languageutils_global.h"
QT_BEGIN_NAMESPACE
class QCryptographicHash;
QT_END_NAMESPACE
namespace LanguageUtils {
class LANGUAGEUTILS_EXPORT ComponentVersion
......@@ -55,6 +59,7 @@ public:
bool isValid() const;
QString toString() const;
void addToHash(QCryptographicHash &hash) const;
};
bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
......
......@@ -28,6 +28,7 @@
****************************************************************************/
#include "fakemetaobject.h"
#include <QCryptographicHash>
using namespace LanguageUtils;
......@@ -62,6 +63,24 @@ QStringList FakeMetaEnum::keys() const
bool FakeMetaEnum::hasKey(const QString &key) const
{ return m_keys.contains(key); }
void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
{
int len = m_name.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
len = m_keys.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QString &key, m_keys) {
len = key.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
}
len = m_values.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (int value, m_values)
hash.addData(reinterpret_cast<const char *>(&value), sizeof(value));
}
FakeMetaMethod::FakeMetaMethod(const QString &name, const QString &returnType)
: m_name(name)
, m_returnType(returnType)
......@@ -109,6 +128,33 @@ int FakeMetaMethod::revision() const
void FakeMetaMethod::setRevision(int r)
{ m_revision = r; }
void FakeMetaMethod::addToHash(QCryptographicHash &hash) const
{
int len = m_name.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
hash.addData(reinterpret_cast<const char *>(&m_methodAccess), sizeof(m_methodAccess));
hash.addData(reinterpret_cast<const char *>(&m_methodTy), sizeof(m_methodTy));
hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
len = m_paramNames.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QString &pName, m_paramNames) {
len = pName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(pName.constData()), len * sizeof(QChar));
}
len = m_paramTypes.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QString &pType, m_paramTypes) {
len = pType.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(pType.constData()), len * sizeof(QChar));
}
len = m_returnType.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_returnType.constData()), len * sizeof(QChar));
}
FakeMetaProperty::FakeMetaProperty(const QString &name, const QString &type, bool isList,
bool isWritable, bool isPointer, int revision)
......@@ -138,6 +184,21 @@ bool FakeMetaProperty::isPointer() const
int FakeMetaProperty::revision() const
{ return m_revision; }
void FakeMetaProperty::addToHash(QCryptographicHash &hash) const
{
int len = m_propertyName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_propertyName.constData()), len * sizeof(QChar));
hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
int flags = (m_isList ? (1 << 0) : 0)
+ (m_isPointer ? (1 << 1) : 0)
+ (m_isWritable ? (1 << 2) : 0);
hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags));
len = m_type.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_type.constData()), len * sizeof(QChar));
}
FakeMetaObject::FakeMetaObject()
{
......@@ -226,8 +287,85 @@ QString FakeMetaObject::attachedTypeName() const
void FakeMetaObject::setAttachedTypeName(const QString &name)
{ m_attachedTypeName = name; }
QByteArray FakeMetaObject::calculateFingerprint() const
{
QCryptographicHash hash(QCryptographicHash::Sha1);
int len = m_className.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_className.constData()), len * sizeof(QChar));
len = m_attachedTypeName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_attachedTypeName.constData()), len * sizeof(QChar));
len = m_defaultPropertyName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_defaultPropertyName.constData()), len * sizeof(QChar));
len = m_enumNameToIndex.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
{
QStringList keys(m_enumNameToIndex.keys());
keys.sort();
foreach (const QString &key, keys) {
len = key.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
int value = m_enumNameToIndex.value(key);
hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
m_enums.at(value).addToHash(hash);
}
}
len = m_exports.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const Export &e, m_exports)
e.addToHash(hash); // normalize order?
len = m_exports.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const FakeMetaMethod &m, m_methods)
m.addToHash(hash); // normalize order?
{
QStringList keys(m_propNameToIdx.keys());
keys.sort();
foreach (const QString &key, keys) {
len = key.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
int value = m_propNameToIdx.value(key);
hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
m_props.at(value).addToHash(hash);
}
}
len = m_superName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(m_superName.constData()), len * sizeof(QChar));
QByteArray res = hash.result();
res.append('F');
return res;
}
void FakeMetaObject::updateFingerprint()
{
m_fingerprint = calculateFingerprint();
}
QByteArray FakeMetaObject::fingerprint() const
{
return m_fingerprint;
}
FakeMetaObject::Export::Export()
: metaObjectRevision(0)
{}
bool FakeMetaObject::Export::isValid() const
{ return version.isValid() || !package.isEmpty() || !type.isEmpty(); }
void FakeMetaObject::Export::addToHash(QCryptographicHash &hash) const
{
int len = package.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(package.constData()), len * sizeof(QChar));
len = type.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(type.constData()), len * sizeof(QChar));
version.addToHash(hash);
hash.addData(reinterpret_cast<const char *>(&metaObjectRevision), sizeof(metaObjectRevision));
}
......@@ -39,6 +39,10 @@
#include <QHash>
#include <QSharedPointer>
QT_BEGIN_NAMESPACE
class QCryptographicHash;
QT_END_NAMESPACE
namespace LanguageUtils {
class LANGUAGEUTILS_EXPORT FakeMetaEnum {
......@@ -60,6 +64,7 @@ public:
int keyCount() const;
QStringList keys() const;
bool hasKey(const QString &key) const;
void addToHash(QCryptographicHash &hash) const;
};
class LANGUAGEUTILS_EXPORT FakeMetaMethod {
......@@ -96,6 +101,7 @@ public:
int revision() const;
void setRevision(int r);
void addToHash(QCryptographicHash &hash) const;
private:
QString m_name;
......@@ -125,6 +131,7 @@ public:
bool isWritable() const;
bool isPointer() const;
int revision() const;
void addToHash(QCryptographicHash &hash) const;
};
class LANGUAGEUTILS_EXPORT FakeMetaObject {
......@@ -144,6 +151,7 @@ public:
int metaObjectRevision;
bool isValid() const;
void addToHash(QCryptographicHash &hash) const;
};
private:
......@@ -157,6 +165,7 @@ private:
QList<FakeMetaMethod> m_methods;
QString m_defaultPropertyName;
QString m_attachedTypeName;
QByteArray m_fingerprint;
public:
FakeMetaObject();
......@@ -195,6 +204,9 @@ public:
QString attachedTypeName() const;
void setAttachedTypeName(const QString &name);
QByteArray calculateFingerprint() const;
void updateFingerprint();
QByteArray fingerprint() const;
};
} // namespace LanguageUtils
......
......@@ -34,8 +34,13 @@
#include <qmljs/parser/qmljslexer_p.h>
#include <qmljs/parser/qmljsparser_p.h>
#include <utils/qtcassert.h>
#include <QCryptographicHash>
#include <QDir>
#include <algorithm>
using namespace QmlJS;
using namespace QmlJS::AST;
......@@ -205,6 +210,16 @@ void Document::setLanguage(Language::Enum l)
_language = l;
}
QString Document::importId() const
{
return path();
}
QByteArray Document::fingerprint() const
{
return _fingerprint;
}
AST::UiProgram *Document::qmlProgram() const
{
return cast<UiProgram *>(_ast);
......@@ -246,6 +261,9 @@ QString Document::source() const
void Document::setSource(const QString &source)
{
_source = source;
QCryptographicHash sha(QCryptographicHash::Sha1);
sha.addData(source.toUtf8());
_fingerprint = sha.result();
}
int Document::editorRevision() const
......@@ -374,21 +392,88 @@ LibraryInfo::LibraryInfo(Status status)
: _status(status)
, _dumpStatus(NoTypeInfo)
{
updateFingerprint();
}
LibraryInfo::LibraryInfo(const QmlDirParser &parser)
LibraryInfo::LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerprint)
: _status(Found)
, _components(parser.components().values())
, _plugins(parser.plugins())
, _typeinfos(parser.typeInfos())
, _fingerprint(fingerprint)
, _dumpStatus(NoTypeInfo)
{
if (_fingerprint.isEmpty())
updateFingerprint();
}
LibraryInfo::~LibraryInfo()
{
}
QByteArray LibraryInfo::calculateFingerprint() const
{
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(reinterpret_cast<const char *>(&_status), sizeof(_status));
int len = _components.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QmlDirParser::Component &component, _components) {
len = component.fileName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(component.fileName.constData()), len * sizeof(QChar));
hash.addData(reinterpret_cast<const char *>(&component.majorVersion), sizeof(component.majorVersion));
hash.addData(reinterpret_cast<const char *>(&component.minorVersion), sizeof(component.minorVersion));
len = component.typeName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(component.typeName.constData()), component.typeName.size() * sizeof(QChar));
int flags = (component.singleton ? (1 << 0) : 0) + (component.internal ? (1 << 1) : 0);
hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags));
}
len = _plugins.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QmlDirParser::Plugin &plugin, _plugins) {
len = plugin.path.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(plugin.path.constData()), len * sizeof(QChar));
len = plugin.name.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(plugin.name.constData()), len * sizeof(QChar));
}
len = _typeinfos.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const QmlDirParser::TypeInfo &typeinfo, _typeinfos) {
len = typeinfo.fileName.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(typeinfo.fileName.constData()), len * sizeof(QChar));
}
len = _metaObjects.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
QList<QByteArray> metaFingerprints;
foreach (const LanguageUtils::FakeMetaObject::ConstPtr &metaObject, _metaObjects)
metaFingerprints.append(metaObject->fingerprint());
std::sort(metaFingerprints.begin(), metaFingerprints.end());
foreach (const QByteArray &fp, metaFingerprints)
hash.addData(fp);
hash.addData(reinterpret_cast<const char *>(&_dumpStatus), sizeof(_dumpStatus));
len = _dumpError.size(); // localization dependent (avoid?)
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(_dumpError.constData()), len * sizeof(QChar));
len = _moduleApis.size();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
foreach (const ModuleApiInfo &moduleInfo, _moduleApis)
moduleInfo.addToHash(hash); // make it order independent?
QByteArray res(hash.result());
res.append('L');
return res;
}
void LibraryInfo::updateFingerprint()
{
_fingerprint = calculateFingerprint();
}
Snapshot::Snapshot()
{
}
......@@ -410,16 +495,33 @@ void Snapshot::insert(const Document::Ptr &document, bool allowInvalid)
if (document && (allowInvalid || document->qmlProgram() || document->jsProgram())) {
const QString fileName = document->fileName();
const QString path = document->path();
remove(fileName);
_documentsByPath[path].append(document);
_documents.insert(fileName, document);
CoreImport cImport;
cImport.importId = document->importId();
cImport.language = document->language();
cImport.possibleExports << Export(ImportKey(ImportType::File, document->path()),
QString(), true);
cImport.fingerprint = document->fingerprint();
_dependencies.addCoreImport(cImport);
}
}
void Snapshot::insertLibraryInfo(const QString &path, const LibraryInfo &info)
{
QTC_CHECK(info.fingerprint() == info.calculateFingerprint());
_libraries.insert(QDir::cleanPath(path), info);
CoreImport cImport;
cImport.importId = path;
cImport.language = Language::Unknown;
foreach (const ModuleApiInfo &moduleInfo, info.moduleApis()) {
ImportKey iKey(ImportType::Library, moduleInfo.uri, moduleInfo.version.majorVersion(),
moduleInfo.version.minorVersion());
cImport.possibleExports << Export(iKey, path, true);
}
cImport.fingerprint = info.fingerprint();
_dependencies.addCoreImport(cImport);
}
void Snapshot::remove(const QString &fileName)
......@@ -473,3 +575,15 @@ LibraryInfo Snapshot::libraryInfo(const QString &path) const
{
return _libraries.value(QDir::cleanPath(path));
}
void ModuleApiInfo::addToHash(QCryptographicHash &hash) const
{
int len = uri.length();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(uri.constData()), len * sizeof(QChar));
version.addToHash(hash);
len = cppName.length();
hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
hash.addData(reinterpret_cast<const char *>(cppName.constData()), len * sizeof(QChar));
}
......@@ -71,6 +71,8 @@ public:
Language::Enum language() const;
void setLanguage(Language::Enum l);
QString importId() const;
QByteArray fingerprint() const;
AST::UiProgram *qmlProgram() const;
AST::Program *jsProgram() const;
AST::ExpressionNode *expression() const;
......@@ -113,6 +115,7 @@ private:
QString _componentName;
QString _source;
QWeakPointer<Document> _ptr;
QByteArray _fingerprint;
int _editorRevision;
Language::Enum _language;
bool _parsedCorrectly;
......@@ -127,6 +130,8 @@ public:
QString uri;
LanguageUtils::ComponentVersion version;
QString cppName;
void addToHash(QCryptographicHash &hash) const;
};
class QMLJS_EXPORT LibraryInfo
......@@ -154,15 +159,21 @@ private:
typedef QList<LanguageUtils::FakeMetaObject::ConstPtr> FakeMetaObjectList;
FakeMetaObjectList _metaObjects;
QList<ModuleApiInfo> _moduleApis;
QByteArray _fingerprint;
PluginTypeInfoStatus _dumpStatus;
QString _dumpError;
public:
explicit LibraryInfo(Status status = NotScanned);
explicit LibraryInfo(const QmlDirParser &parser);
explicit LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerprint = QByteArray());
~LibraryInfo();
QByteArray calculateFingerprint() const;
void updateFingerprint();
QByteArray fingerprint() const
{ return _fingerprint; }
QList<QmlDirParser::Component> components() const
{ return _components; }
......
......@@ -29,6 +29,7 @@
#include "qmljsimportdependencies.h"
#include "qmljsinterpreter.h"
#include "qmljsqrcparser.h"
#include <utils/qtcassert.h>
#include <utils/function.h>
......@@ -113,6 +114,46 @@ ImportKey::ImportKey()
minorVersion(LanguageUtils::ComponentVersion::NoVersion)
{ }
ImportKey::ImportKey(const ImportInfo &info)
: type(info.type())
, majorVersion(info.version().majorVersion())
, minorVersion(info.version().minorVersion())
{
splitPath = QFileInfo(info.path()).canonicalFilePath().split(QLatin1Char('/'),
QString::KeepEmptyParts);
}
ImportKey::ImportKey(ImportType::Enum type, const QString &path, int majorVersion, int minorVersion)
: type(type)
, majorVersion(majorVersion)
, minorVersion(minorVersion)
{
switch (type) {
case ImportType::Library:
splitPath = path.split(QLatin1Char('.'));
break;
case ImportType::ImplicitDirectory:
case ImportType::Directory:
splitPath = path.split(QLatin1Char('/'));
if (splitPath.length() > 1 && splitPath.last().isEmpty())
splitPath.removeLast();
break;
case ImportType::File:
case ImportType::QrcFile:
splitPath = QrcParser::normalizedQrcFilePath(path).split(QLatin1Char('/'));
break;
case ImportType::QrcDirectory:
splitPath = QrcParser::normalizedQrcDirectoryPath(path).split(QLatin1Char('/'));
if (splitPath.length() > 1 && splitPath.last().isEmpty())
splitPath.removeLast();
break;
case ImportType::Invalid:
case ImportType::UnknownFile:
splitPath = path.split(QLatin1Char('/'));
break;
}
}
void ImportKey::addToHash(QCryptographicHash &hash) const
{
hash.addData(reinterpret_cast<const char *>(&type), sizeof(type));
......@@ -153,15 +194,6 @@ ImportKey ImportKey::flatKey() const {
return res;
}
ImportKey::ImportKey(const ImportInfo &info)
: type(info.type())
, majorVersion(info.version().majorVersion())
, minorVersion(info.version().minorVersion())
{
splitPath = QFileInfo(info.path()).canonicalFilePath().split(QChar::fromLatin1('/'),
QString::KeepEmptyParts);
}
QString ImportKey::path() const
{
QString res = splitPath.join(QString::fromLatin1("/"));
......@@ -430,8 +462,8 @@ bool operator !=(const Export &i1, const Export &i2)
CoreImport::CoreImport() : language(Language::Qml) { }
CoreImport::CoreImport(const QString &importId, QList<Export> possibleExports,
Language::Enum language, QByteArray fingerprint)
CoreImport::CoreImport(const QString &importId, const QList<Export> &possibleExports,
Language::Enum language, const QByteArray &fingerprint)
: importId(importId), possibleExports(possibleExports), language(language),
fingerprint(fingerprint)
{ }
......@@ -463,7 +495,7 @@ MatchedImport::MatchedImport()
{ }
MatchedImport::MatchedImport(ImportMatchStrength matchStrength, ImportKey importKey,
QString coreImportId)
const QString &coreImportId)
: matchStrength(matchStrength), importKey(importKey), coreImportId(coreImportId)
{ }
......@@ -474,8 +506,11 @@ int MatchedImport::compare(const MatchedImport &o) const {
res = importKey.compare(o.importKey);
if (res != 0)
return res;
res = coreImportId.compare(o.coreImportId);
return res;
if (coreImportId < o.coreImportId)
return -1;
if (coreImportId > o.coreImportId)
return 1;
return 0;
}
bool operator ==(const MatchedImport &m1, const MatchedImport &m2)
......
......@@ -80,6 +80,11 @@ bool operator <(const ImportMatchStrength &m1, const ImportMatchStrength &m2);
* \brief The ImportKey class represent an import (or export), and can be used as hash key
*
* This represent only what is to be imported, *not* how (i.e. no as clause)
*
* Order is defined so that files in the same directory are contiguous, and different
* ImportKind are separated.
* This is used to efficiently iterate just on library imports, or just on a directory
* while preserving space.
*/
class QMLJS_EXPORT ImportKey