Commit bd827512 authored by dt's avatar dt
Browse files

Fixes: Mem leak in the pro file parser.

Details:  ProFileCache be gone, as is ManagedProFile, not to be missed.
Drastically simplified how ownership works.
parent 4eaa9f50
......@@ -1519,12 +1519,9 @@ bool ProFileEvaluator::Private::evaluateFile(const QString &fileName, bool *resu
if (pro) {
m_profileStack.push(pro);
ok = (currentProFile() ? pro->Accept(this) : false);
if (ok) {
if (m_profileStack.count() > 0) {
ProFile *pro = m_profileStack.pop();
q->releaseParsedProFile(pro);
}
}
m_profileStack.pop();
q->releaseParsedProFile(pro);
if (result)
*result = true;
} else {
......
/***************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/
#include "qt4project.h"
#include "qt4nodes.h"
#include "qt4projectmanager.h"
#include "profilecache.h"
#include "proparser/prowriter.h"
#include "proitems.h"
#include "qt4projectmanagerconstants.h"
#include <coreplugin/filemanager.h>
#include <utils/reloadpromptutils.h>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
#include <QtGui/QMainWindow>
#include <QtCore/QDir>
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
namespace {
bool debug = false;
}
namespace Qt4ProjectManager {
namespace Internal {
class ManagedProjectFile
: public Core::IFile
{
Q_OBJECT
public:
ManagedProjectFile(ProFileCache *parent, ProFile *profile);
virtual ~ManagedProjectFile();
ProFile *profile();
void setProfile(ProFile *profile);
bool isOutOfSync() { return m_outOfSync; }
void setOutOfSync(bool state) { m_outOfSync = state; }
// Core::IFile
bool save(const QString &fileName = QString());
QString fileName() const;
QString defaultPath() const;
QString suggestedFileName() const;
virtual QString mimeType() const;
bool isModified() const;
bool isReadOnly() const;
bool isSaveAsAllowed() const;
void modified(ReloadBehavior *behavior);
signals:
void changed();
private:
const QString m_mimeType;
ProFile *m_profile;
ProFileCache *m_cache;
bool m_outOfSync;
};
} // namespace Internal
} // namespace Qt4ProFileNodemanager
ManagedProjectFile::ManagedProjectFile(ProFileCache *parent, ProFile *profile) :
Core::IFile(parent),
m_mimeType(QLatin1String(Qt4ProjectManager::Constants::PROFILE_MIMETYPE)),
m_profile(profile),
m_cache(parent),
m_outOfSync(false)
{
}
ManagedProjectFile::~ManagedProjectFile()
{
delete m_profile;
}
ProFile *ManagedProjectFile::profile()
{
return m_profile;
}
void ManagedProjectFile::setProfile(ProFile *profile)
{
m_outOfSync = false;
if (profile == m_profile)
return;
delete m_profile;
m_profile = profile;
}
bool ManagedProjectFile::save(const QString &)
{
if (!m_profile)
return false;
ProWriter pw;
bool ok = pw.write(m_profile, m_profile->fileName());
m_profile->setModified(false);
m_cache->notifyModifiedChanged(m_profile);
return ok;
}
QString ManagedProjectFile::fileName() const
{
return QDir::cleanPath(m_profile->fileName());
}
QString ManagedProjectFile::defaultPath() const
{
QFileInfo fi(fileName());
return fi.absolutePath();
}
QString ManagedProjectFile::suggestedFileName() const
{
QFileInfo fi(fileName());
return fi.fileName();
}
bool ManagedProjectFile::isModified() const
{
return m_profile->isModified();
}
bool ManagedProjectFile::isReadOnly() const
{
QFileInfo fi(fileName());
return !fi.isWritable();
}
bool ManagedProjectFile::isSaveAsAllowed() const
{
return false;
}
void ManagedProjectFile::modified(ReloadBehavior *behavior)
{
switch (*behavior) {
case Core::IFile::ReloadNone:
case Core::IFile::ReloadPermissions:
return;
case Core::IFile::ReloadAll:
m_cache->notifyChanged(QSet<ProFile *>() << m_profile, true);
return;
case Core::IFile::AskForReload:
break;
}
const QString prompt = tr("The project file %1 has changed outside Qt Creator. Do you want to reload it?").arg(m_profile->fileName());
switch (const Core::Utils::ReloadPromptAnswer answer = Core::Utils::reloadPrompt(tr("File Changed"), prompt, m_cache->manager()->core()->mainWindow())) {
case Core::Utils::ReloadAll:
case Core::Utils::ReloadCurrent:
m_cache->notifyChanged(QSet<ProFile *>() << m_profile, true);
if (answer == Core::Utils::ReloadAll)
*behavior = Core::IFile::ReloadAll;
break;
case Core::Utils::ReloadSkipCurrent:
break;
case Core::Utils::ReloadNone:
*behavior = Core::IFile::ReloadNone;
break;
}
}
QString ManagedProjectFile::mimeType() const
{
return m_mimeType;
}
#include "profilecache.moc"
ProFileCache::ProFileCache(Qt4Manager *manager)
: QObject(manager)
{
m_manager = manager;
}
ProFileCache::~ProFileCache()
{
qDeleteAll(m_profiles.values());
m_profiles.clear();
m_projects.clear();
}
void ProFileCache::notifyModifiedChanged(ProFile *profile)
{
QList<Qt4ProFileNode *> pros = m_projects.values(profile->fileName());
for (int i=0; i<pros.count(); ++i) {
Qt4ProFileNode *pro = pros.at(i);
pro->update();
}
}
void ProFileCache::notifyChanged(const QSet<ProFile *> &profiles, bool external)
{
QList<Qt4ProFileNode *> notifyProjects;
foreach (ProFile *profile, profiles) {
QList<Qt4ProFileNode *> pros = m_projects.values(profile->fileName());
if (external) {
ManagedProjectFile *file = m_profiles.value(profile->fileName());
if (file)
file->setOutOfSync(true);
}
QList<Qt4ProFileNode *>::const_iterator i = pros.constBegin();
for (; i != pros.constEnd(); ++i) {
if (!notifyProjects.contains(*i))
notifyProjects << *i;
}
}
QList<Qt4ProFileNode *>::const_iterator i = notifyProjects.constBegin();
while (i != notifyProjects.constEnd()) {
(*i)->update();
++i;
}
}
void ProFileCache::updateDependencies(const QSet<ProFile *> &files, Qt4ProFileNode *project)
{
// just remove and add files that have changed
const QSet<QString> &oldFiles = m_projects.keys(project).toSet();
QSet<QString> newFiles;
QList<Core::IFile *> addedFiles;
foreach (ProFile *file, files) {
newFiles << file->fileName();
if (!m_profiles.contains(file->fileName())) {
ManagedProjectFile *profile = new ManagedProjectFile(this, file);
m_profiles.insert(file->fileName(), profile);
if (debug)
qDebug() << "ProFileCache - inserting file " << file->fileName();
addedFiles << profile;
} else {
ManagedProjectFile *profile = m_profiles.value(file->fileName());
profile->setProfile(file);
}
}
m_manager->core()->fileManager()->addFiles(addedFiles);
if (oldFiles.isEmpty()) {
connect(project, SIGNAL(destroyed(QObject *)),
this, SLOT(removeProject(QObject *)));
}
foreach (const QString &profile, (oldFiles - newFiles).toList()) {
removeFile(profile, project);
}
foreach (const QString &profile, (newFiles - oldFiles).toList()) {
m_projects.insertMulti(profile, project);
}
}
QStringList ProFileCache::dependencies(Qt4ProFileNode *project) const
{
return m_projects.keys(project);
}
ProFile *ProFileCache::proFile(const QString &file) const
{
QSet<ProFile *> profiles = proFiles(QStringList(file));
if (profiles.isEmpty())
return 0;
return profiles.toList().first();
}
QSet<ProFile *> ProFileCache::proFiles(const QStringList &files) const
{
QSet<ProFile *> results;
foreach (const QString &file, files) {
ManagedProjectFile *managedFile = m_profiles.value(file, 0);
if (managedFile && !managedFile->isOutOfSync()) {
results << managedFile->profile();
}
}
return results;
}
QSet<ProFile *> ProFileCache::proFiles(Qt4ProFileNode *project) const
{
return proFiles(m_projects.keys(project));
}
Core::IFile *ProFileCache::fileInterface(const QString &file)
{
return m_profiles.value(file);
}
void ProFileCache::removeFile(const QString &file, Qt4ProFileNode *proj)
{
QList<Qt4ProFileNode*> projects = m_projects.values(file);
m_projects.remove(file);
projects.removeAll(proj);
if (!projects.isEmpty()) {
foreach (Qt4ProFileNode *p, projects) {
m_projects.insert(file, p);
}
} else {
ManagedProjectFile *mp = m_profiles.value(file, 0);
if (debug)
qDebug() << "ProFileCache - removing file " << file;
m_manager->core()->fileManager()->removeFile(mp);
m_profiles.remove(file);
delete mp;
}
}
void ProFileCache::removeProject(QObject *obj)
{
// Cannot use qobject_cast here because
// it is triggered by destroyed signal
Qt4ProFileNode *proj = static_cast<Qt4ProFileNode*>(obj);
QStringList files = m_projects.keys(proj);
foreach (const QString &file, files) {
removeFile(file, proj);
}
}
/***************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Qt Software Information (qt-info@nokia.com)
**
**
** Non-Open Source Usage
**
** Licensees may use this file in accordance with the Qt Beta Version
** License Agreement, Agreement version 2.2 provided with the Software or,
** alternatively, in accordance with the terms contained in a written
** agreement between you and Nokia.
**
** GNU General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU General
** Public License versions 2.0 or 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the packaging
** of this file. Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt GPL Exception
** version 1.2, included in the file GPL_EXCEPTION.txt in this package.
**
***************************************************************************/
#ifndef PROFILECACHE_H
#define PROFILECACHE_H
#include <QtCore/QObject>
#include <QtCore/QSet>
#include <QtCore/QStringList>
#include <QtCore/QMap>
QT_BEGIN_NAMESPACE
class ProFile;
QT_END_NAMESPACE
namespace Core {
class IFile;
}
namespace Qt4ProjectManager {
//class Qt4Project;
class Qt4Manager;
namespace Internal {
class Qt4ProFileNode;
class ManagedProjectFile;
class ProFileCache : public QObject
{
Q_OBJECT
public:
ProFileCache(Qt4Manager *manager);
~ProFileCache();
// does not result in a reparse or resolve
void notifyModifiedChanged(ProFile *profile);
// if external is true we need to reparse, it it's false we only resolve
void notifyChanged(const QSet<ProFile *> &profiles, bool external = false);
void updateDependencies(const QSet<ProFile *> &files, Qt4ProFileNode *projectNode);
QStringList dependencies(Qt4ProFileNode *projectNode) const;
ProFile *proFile(const QString &filename) const;
QSet<ProFile *> proFiles(const QStringList &files) const;
QSet<ProFile *> proFiles(Qt4ProFileNode *projectNode) const;
Core::IFile *fileInterface(const QString &filename);
inline Qt4Manager *manager() const { return m_manager; }
protected slots:
void removeProject(QObject *obj);
private:
void removeFile(const QString &profile, Qt4ProFileNode *projectNode);
Qt4Manager *m_manager;
QMultiMap<QString, Qt4ProFileNode *> m_projects;
QMap<QString, ManagedProjectFile *> m_profiles;
};
} // namespace Internal
} // namespace Qt4ProjectManager
#endif // PROFILECACHE_H
......@@ -36,7 +36,6 @@
#include "profilehighlighter.h"
#include "qt4projectmanager.h"
#include "qt4projectmanagerconstants.h"
#include "profilecache.h"
#include "profileeditorfactory.h"
#include "proeditormodel.h"
#include "procommandmanager.h"
......@@ -51,6 +50,7 @@
#include <QtCore/QFileInfo>
#include <QtGui/QTextEdit>
#include <QtGui/QHeaderView>
#include <QtCore/QDebug>
using namespace ExtensionSystem;
using namespace Core;
......@@ -150,9 +150,7 @@ ProFileDocument::ProFileDocument(Qt4Manager *manager)
bool ProFileDocument::save(const QString &name)
{
if (BaseTextDocument::save(name)) {
ProFile *profile = m_manager->proFileCache()->proFile(name);
if (profile)
m_manager->proFileCache()->notifyChanged(QSet<ProFile*>() << profile, true);
m_manager->notifyChanged(name);
return true;
}
return false;
......
......@@ -32,7 +32,6 @@
***************************************************************************/
#include "profilereader.h"
#include "profilecache.h"
#include <QtCore/QDir>
#include <QtCore/QDebug>
......@@ -40,11 +39,16 @@
using namespace Qt4ProjectManager;
using namespace Qt4ProjectManager::Internal;
ProFileReader::ProFileReader(ProFileCache *cache)
: m_cache(cache)
ProFileReader::ProFileReader()
{
}
ProFileReader::~ProFileReader()
{
foreach(ProFile *pf, m_proFiles)
delete pf;
}
void ProFileReader::setQtVersion(QtVersion *qtVersion) {
QHash<QString, QStringList> additionalVariables;
additionalVariables.insert(QString("QT_BUILD_TREE"), QStringList() << qtVersion->path());
......@@ -72,6 +76,7 @@ bool ProFileReader::readProFile(const QString &fileName)
return false;
}
m_includeFiles.insert(fn, pro);
m_proFiles.append(pro);
return accept(pro);
}
......@@ -84,23 +89,23 @@ ProFile *ProFileReader::parsedProFile(const QString &fileName)
ProFile *pro = ProFileEvaluator::parsedProFile(fn);
if (pro) {
m_includeFiles.insert(fn, pro);
m_proFiles.append(pro);
}
return pro;
}
void ProFileReader::releaseParsedProFile(ProFile */*proFile*/)
void ProFileReader::releaseParsedProFile(ProFile *)
{
return;
}
ProFile *ProFileReader::proFileFromCache(const QString &fileName) const
{
QString fn = QFileInfo(fileName).filePath();
ProFile *pro = 0;
if (m_includeFiles.contains(fn))
pro = m_includeFiles.value(fn);
else
pro = m_cache->proFile(fn); // this can expand to null
return pro;
}
......
......@@ -43,14 +43,14 @@
namespace Qt4ProjectManager {
namespace Internal {
class ProFileCache;
class ProFileReader : public QObject, public ProFileEvaluator
{
Q_OBJECT
public:
ProFileReader(ProFileCache *cache);
ProFileReader();
~ProFileReader();
void setQtVersion(QtVersion *qtVersion);
bool readProFile(const QString &fileName);
......@@ -63,7 +63,7 @@ public:
const QString &baseDirectory,
PathValuesMode mode,
const ProFile *pro = 0) const;
ProFile *proFileFromCache(const QString &fileName) const;
signals:
void errorFound(const QString &error);
......@@ -75,9 +75,8 @@ private:
virtual void errorMessage(const QString &msg);
private:
ProFile *proFileFromCache(const QString &fileName) const;
ProFileCache *m_cache;
QMap<QString, ProFile *> m_includeFiles;
QList<ProFile *> m_proFiles;
};
} // namespace Internal
......