QmlJsDebugger: Handle breakpoints in qrc:// resources more gracefully

Reviewed-by: Christiaan Janssen
This commit is contained in:
Kai Koehne
2011-02-25 11:57:34 +01:00
parent 844c34513d
commit 20189574f6
3 changed files with 103 additions and 85 deletions

View File

@@ -46,7 +46,6 @@
#include <QtCore/qdebug.h> #include <QtCore/qdebug.h>
#include <QtCore/qcoreapplication.h> #include <QtCore/qcoreapplication.h>
#include <QtCore/qset.h> #include <QtCore/qset.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qurl.h> #include <QtCore/qurl.h>
#include <QtScript/qscriptcontextinfo.h> #include <QtScript/qscriptcontextinfo.h>
#include <QtScript/qscriptengine.h> #include <QtScript/qscriptengine.h>
@@ -82,19 +81,19 @@ QDataStream &operator<<(QDataStream &s, const JSAgentWatchData &data)
struct JSAgentStackData struct JSAgentStackData
{ {
QByteArray functionName; QByteArray functionName;
QByteArray fileName; QByteArray fileUrl;
qint32 lineNumber; qint32 lineNumber;
}; };
QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data) QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data)
{ {
return s << data.functionName << data.fileName << data.lineNumber; return s << data.functionName << data.fileUrl << data.lineNumber;
} }
struct JSAgentBreakpointData struct JSAgentBreakpointData
{ {
QByteArray functionName; QByteArray functionName;
QByteArray fileName; QByteArray fileUrl;
qint32 lineNumber; qint32 lineNumber;
}; };
@@ -102,22 +101,22 @@ typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints;
QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data)
{ {
return s << data.functionName << data.fileName << data.lineNumber; return s << data.functionName << data.fileUrl << data.lineNumber;
} }
QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data)
{ {
return s >> data.functionName >> data.fileName >> data.lineNumber; return s >> data.functionName >> data.fileUrl >> data.lineNumber;
} }
bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2)
{ {
return b1.lineNumber == b2.lineNumber && b1.fileName == b2.fileName; return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl;
} }
uint qHash(const JSAgentBreakpointData &b) uint qHash(const JSAgentBreakpointData &b)
{ {
return b.lineNumber ^ qHash(b.fileName); return b.lineNumber ^ qHash(b.fileUrl);
} }
class JSDebuggerAgentPrivate class JSDebuggerAgentPrivate
@@ -316,7 +315,7 @@ void JSDebuggerAgent::scriptLoad(qint64 id, const QString &program,
const QString &fileName, int) const QString &fileName, int)
{ {
Q_UNUSED(program); Q_UNUSED(program);
d->filenames.insert(id, QUrl(fileName).toLocalFile()); d->filenames.insert(id, fileName);
} }
/*! /*!
@@ -368,6 +367,12 @@ void JSDebuggerAgent::positionChange(qint64 scriptId, int lineNumber, int column
d->positionChange(scriptId, lineNumber, columnNumber); d->positionChange(scriptId, lineNumber, columnNumber);
} }
QString fileName(const QString &fileUrl)
{
int lastDelimiterPos = fileUrl.lastIndexOf(QLatin1Char('/'));
return fileUrl.mid(lastDelimiterPos, fileUrl.size() - lastDelimiterPos);
}
void JSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int columnNumber) void JSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int columnNumber)
{ {
Q_UNUSED(columnNumber); Q_UNUSED(columnNumber);
@@ -382,7 +387,7 @@ void JSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int
QScriptContextInfo info(ctx); QScriptContextInfo info(ctx);
if (it == filenames.constEnd()) { if (it == filenames.constEnd()) {
// It is possible that the scripts are loaded before the agent is attached // It is possible that the scripts are loaded before the agent is attached
QString filename = QUrl(info.fileName()).toLocalFile(); QString filename = info.fileName();
JSAgentStackData frame; JSAgentStackData frame;
frame.functionName = info.functionName().toUtf8(); frame.functionName = info.functionName().toUtf8();
@@ -392,7 +397,7 @@ void JSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int
} }
const QString filePath = it->toUtf8(); const QString filePath = it->toUtf8();
JSAgentBreakpoints bps = fileNameToBreakpoints.values(QFileInfo(filePath).fileName()).toSet(); JSAgentBreakpoints bps = fileNameToBreakpoints.values(fileName(filePath)).toSet();
foreach (const JSAgentBreakpointData &bp, bps) { foreach (const JSAgentBreakpointData &bp, bps) {
if (bp.lineNumber == lineNumber) { if (bp.lineNumber == lineNumber) {
@@ -477,7 +482,7 @@ void JSDebuggerAgentPrivate::messageReceived(const QByteArray &message)
fileNameToBreakpoints.clear(); fileNameToBreakpoints.clear();
foreach (const JSAgentBreakpointData &bp, breakpoints) { foreach (const JSAgentBreakpointData &bp, breakpoints) {
fileNameToBreakpoints.insert(QFileInfo(bp.fileName).fileName(), bp); fileNameToBreakpoints.insert(fileName(bp.fileUrl), bp);
} }
//qDebug() << "BREAKPOINTS"; //qDebug() << "BREAKPOINTS";
@@ -621,7 +626,8 @@ void JSDebuggerAgentPrivate::stopped()
// if the line number is unknown, fallback to the function line number // if the line number is unknown, fallback to the function line number
if (frame.lineNumber == -1) if (frame.lineNumber == -1)
frame.lineNumber = info.functionStartLineNumber(); frame.lineNumber = info.functionStartLineNumber();
frame.fileName = QUrl(info.fileName()).toLocalFile().toUtf8();
frame.fileUrl = info.fileName().toUtf8();
backtrace.append(frame); backtrace.append(frame);
} }
QList<JSAgentWatchData> watches; QList<JSAgentWatchData> watches;

View File

@@ -57,6 +57,7 @@
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/abstractprocess.h> #include <utils/abstractprocess.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/fileinprojectfinder.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h> #include <coreplugin/helpmanager.h>
@@ -93,31 +94,49 @@ namespace Internal {
struct JSAgentBreakpointData struct JSAgentBreakpointData
{ {
QByteArray functionName; QByteArray functionName;
QByteArray fileName; QByteArray fileUrl;
qint32 lineNumber;
};
struct JSAgentStackData
{
QByteArray functionName;
QByteArray fileUrl;
qint32 lineNumber; qint32 lineNumber;
}; };
uint qHash(const JSAgentBreakpointData &b) uint qHash(const JSAgentBreakpointData &b)
{ {
return b.lineNumber ^ qHash(b.fileName); return b.lineNumber ^ qHash(b.fileUrl);
} }
QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data) QDataStream &operator<<(QDataStream &s, const JSAgentBreakpointData &data)
{ {
return s << data.functionName << data.fileName << data.lineNumber; return s << data.functionName << data.fileUrl << data.lineNumber;
}
QDataStream &operator<<(QDataStream &s, const JSAgentStackData &data)
{
return s << data.functionName << data.fileUrl << data.lineNumber;
} }
QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data) QDataStream &operator>>(QDataStream &s, JSAgentBreakpointData &data)
{ {
return s >> data.functionName >> data.fileName >> data.lineNumber; return s >> data.functionName >> data.fileUrl >> data.lineNumber;
}
QDataStream &operator>>(QDataStream &s, JSAgentStackData &data)
{
return s >> data.functionName >> data.fileUrl >> data.lineNumber;
} }
bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2) bool operator==(const JSAgentBreakpointData &b1, const JSAgentBreakpointData &b2)
{ {
return b1.lineNumber == b2.lineNumber && b1.fileName == b2.fileName; return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl;
} }
typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints;
typedef QList<JSAgentStackData> JSAgentStackFrames;
static QDataStream &operator>>(QDataStream &s, WatchData &data) static QDataStream &operator>>(QDataStream &s, WatchData &data)
@@ -136,19 +155,6 @@ static QDataStream &operator>>(QDataStream &s, WatchData &data)
return s; return s;
} }
static QDataStream &operator>>(QDataStream &s, StackFrame &frame)
{
frame = StackFrame();
QByteArray function;
QByteArray file;
s >> function >> file >> frame.line;
frame.function = QString::fromUtf8(function);
frame.file = QString::fromUtf8(file);
frame.usable = QFileInfo(frame.file).isReadable();
return s;
}
class QmlEnginePrivate class QmlEnginePrivate
{ {
public: public:
@@ -159,6 +165,7 @@ private:
int m_ping; int m_ping;
QmlAdapter m_adapter; QmlAdapter m_adapter;
ApplicationLauncher m_applicationLauncher; ApplicationLauncher m_applicationLauncher;
Utils::FileInProjectFinder fileFinder;
}; };
QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q) QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q)
@@ -183,14 +190,6 @@ QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters,
QmlEngine::~QmlEngine() QmlEngine::~QmlEngine()
{} {}
void QmlEngine::gotoLocation(const Location &loc0)
{
Location loc = loc0;
if (isShadowBuildProject())
loc.setFileName(fromShadowBuildFilename(loc0.fileName()));
DebuggerEngine::gotoLocation(loc);
}
void QmlEngine::setupInferior() void QmlEngine::setupInferior()
{ {
QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state());
@@ -576,18 +575,8 @@ void QmlEngine::attemptBreakpointSynchronization()
if (handler->state(id) == BreakpointInsertRequested) { if (handler->state(id) == BreakpointInsertRequested) {
handler->notifyBreakpointInsertProceeding(id); handler->notifyBreakpointInsertProceeding(id);
} }
QString processedFilename = handler->fileName(id);
#ifdef Q_OS_MACX
// Qt Quick Applications by default copy the qml directory
// to buildDir()/X.app/Contents/Resources
const QString applicationBundleDir
= QFileInfo(startParameters().executable).absolutePath() + "/../..";
processedFilename = mangleFilenamePaths(handler->fileName(id), startParameters().projectDir, applicationBundleDir + "/Contents/Resources");
#endif
if (isShadowBuildProject())
processedFilename = toShadowBuildFilename(processedFilename);
JSAgentBreakpointData bp; JSAgentBreakpointData bp;
bp.fileName = processedFilename.toUtf8(); bp.fileUrl = QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8();
bp.lineNumber = handler->lineNumber(id); bp.lineNumber = handler->lineNumber(id);
bp.functionName = handler->functionName(id).toUtf8(); bp.functionName = handler->functionName(id).toUtf8();
breakpoints.insert(bp); breakpoints.insert(bp);
@@ -606,7 +595,7 @@ void QmlEngine::attemptBreakpointSynchronization()
QStringList breakPointsStr; QStringList breakPointsStr;
foreach (const JSAgentBreakpointData &bp, breakpoints) { foreach (const JSAgentBreakpointData &bp, breakpoints) {
breakPointsStr << QString("('%1' '%2' %3)").arg(QString(bp.functionName), breakPointsStr << QString("('%1' '%2' %3)").arg(QString(bp.functionName),
QString(bp.fileName), QString::number(bp.lineNumber)); QString(bp.fileUrl), QString::number(bp.lineNumber));
} }
logMessage(LogSend, QString("%1 [%2]").arg(QString(cmd), breakPointsStr.join(", "))); logMessage(LogSend, QString("%1 [%2]").arg(QString(cmd), breakPointsStr.join(", ")));
@@ -754,6 +743,38 @@ unsigned QmlEngine::debuggerCapabilities() const
| AddWatcherCapability;*/ | AddWatcherCapability;*/
} }
QString QmlEngine::toFileInProject(const QString &fileUrl)
{
if (fileUrl.isEmpty())
return fileUrl;
const QString path = QUrl(fileUrl).path();
// Try to find shadow-build file in source dir first
if (!QUrl(fileUrl).toLocalFile().isEmpty()
&& isShadowBuildProject()) {
const QString sourcePath = fromShadowBuildFilename(path);
if (QFileInfo(sourcePath).exists())
return sourcePath;
}
// Try whether file is absolute & exists
if (QFileInfo(path).isAbsolute()
&& QFileInfo(path).exists()) {
return path;
}
if (d->fileFinder.projectDirectory().isEmpty())
d->fileFinder.setProjectDirectory(startParameters().projectDir);
// Try to find file with biggest common path in source directory
bool fileFound = false;
QString fileInProject = d->fileFinder.findFile(path, &fileFound);
if (fileFound)
return fileInProject;
return fileUrl;
}
void QmlEngine::messageReceived(const QByteArray &message) void QmlEngine::messageReceived(const QByteArray &message)
{ {
QByteArray rwData = message; QByteArray rwData = message;
@@ -769,7 +790,7 @@ void QmlEngine::messageReceived(const QByteArray &message)
QString logString = QString::fromLatin1(command); QString logString = QString::fromLatin1(command);
StackFrames stackFrames; JSAgentStackFrames stackFrames;
QList<WatchData> watches; QList<WatchData> watches;
QList<WatchData> locals; QList<WatchData> locals;
stream >> stackFrames >> watches >> locals; stream >> stackFrames >> watches >> locals;
@@ -777,12 +798,20 @@ void QmlEngine::messageReceived(const QByteArray &message)
logString += QString::fromLatin1(" (%1 stack frames) (%2 watches) (%3 locals)"). logString += QString::fromLatin1(" (%1 stack frames) (%2 watches) (%3 locals)").
arg(stackFrames.size()).arg(watches.size()).arg(locals.size()); arg(stackFrames.size()).arg(watches.size()).arg(locals.size());
for (int i = 0; i != stackFrames.size(); ++i) StackFrames ideStackFrames;
stackFrames[i].level = i + 1; for (int i = 0; i != stackFrames.size(); ++i) {
StackFrame frame;
frame.line = stackFrames.at(i).lineNumber;
frame.function = stackFrames.at(i).functionName;
frame.file = toFileInProject(stackFrames.at(i).fileUrl);
frame.usable = QFileInfo(frame.file).isReadable();
frame.level = i + 1;
ideStackFrames << frame;
}
if (stackFrames.size() && stackFrames.back().function == "<global>") if (ideStackFrames.size() && ideStackFrames.back().function == "<global>")
stackFrames.takeLast(); ideStackFrames.takeLast();
stackHandler()->setFrames(stackFrames); stackHandler()->setFrames(ideStackFrames);
watchHandler()->beginCycle(); watchHandler()->beginCycle();
bool needPing = false; bool needPing = false;
@@ -807,10 +836,11 @@ void QmlEngine::messageReceived(const QByteArray &message)
} }
} }
if (needPing) if (needPing) {
sendPing(); sendPing();
else } else {
watchHandler()->endCycle(); watchHandler()->endCycle();
}
bool becauseOfException; bool becauseOfException;
stream >> becauseOfException; stream >> becauseOfException;
@@ -829,7 +859,7 @@ void QmlEngine::messageReceived(const QByteArray &message)
? tr("<p>An uncaught exception occurred:</p><p>%1</p>") ? tr("<p>An uncaught exception occurred:</p><p>%1</p>")
.arg(Qt::escape(error)) .arg(Qt::escape(error))
: tr("<p>An uncaught exception occurred in <i>%1</i>:</p><p>%2</p>") : tr("<p>An uncaught exception occurred in <i>%1</i>:</p><p>%2</p>")
.arg(stackFrames.value(0).file, Qt::escape(error)); .arg(stackFrames.value(0).fileUrl, Qt::escape(error));
showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg); showMessageBox(QMessageBox::Information, tr("Uncaught Exception"), msg);
} else { } else {
// //
@@ -839,14 +869,10 @@ void QmlEngine::messageReceived(const QByteArray &message)
QString function; QString function;
int line = -1; int line = -1;
if (!stackFrames.isEmpty()) { if (!ideStackFrames.isEmpty()) {
file = stackFrames.at(0).file; file = ideStackFrames.at(0).file;
line = stackFrames.at(0).line; line = ideStackFrames.at(0).line;
function = stackFrames.at(0).function; function = ideStackFrames.at(0).function;
if (isShadowBuildProject()) {
file = fromShadowBuildFilename(file);
}
} }
BreakHandler *handler = breakHandler(); BreakHandler *handler = breakHandler();
@@ -865,8 +891,8 @@ void QmlEngine::messageReceived(const QByteArray &message)
logMessage(LogReceive, logString); logMessage(LogReceive, logString);
} }
if (!stackFrames.isEmpty()) if (!ideStackFrames.isEmpty())
gotoLocation(stackFrames.value(0)); gotoLocation(ideStackFrames.value(0));
} else if (command == "RESULT") { } else if (command == "RESULT") {
WatchData data; WatchData data;
@@ -977,19 +1003,6 @@ QString QmlEngine::qmlImportPath() const
return startParameters().environment.value("QML_IMPORT_PATH"); return startParameters().environment.value("QML_IMPORT_PATH");
} }
QString QmlEngine::toShadowBuildFilename(const QString &filename) const
{
QString newFilename = filename;
QString importPath = qmlImportPath();
newFilename = mangleFilenamePaths(filename, startParameters().projectDir, startParameters().projectBuildDir);
if (newFilename == filename && !importPath.isEmpty()) {
newFilename = mangleFilenamePaths(filename, startParameters().projectDir, importPath);
}
return newFilename;
}
QString QmlEngine::mangleFilenamePaths(const QString &filename, QString QmlEngine::mangleFilenamePaths(const QString &filename,
const QString &oldBasePath, const QString &newBasePath) const const QString &oldBasePath, const QString &newBasePath) const
{ {

View File

@@ -58,7 +58,6 @@ public:
void handleRemoteSetupDone(int gdbServerPort, int qmlPort); void handleRemoteSetupDone(int gdbServerPort, int qmlPort);
void handleRemoteSetupFailed(const QString &message); void handleRemoteSetupFailed(const QString &message);
void gotoLocation(const Location &location);
bool canDisplayTooltip() const; bool canDisplayTooltip() const;
void showMessage(const QString &msg, int channel = LogDebug, void showMessage(const QString &msg, int channel = LogDebug,
@@ -145,7 +144,6 @@ private:
QString fromShadowBuildFilename(const QString &filename) const; QString fromShadowBuildFilename(const QString &filename) const;
QString mangleFilenamePaths(const QString &filename, QString mangleFilenamePaths(const QString &filename,
const QString &oldBasePath, const QString &newBasePath) const; const QString &oldBasePath, const QString &newBasePath) const;
QString toShadowBuildFilename(const QString &filename) const;
QString qmlImportPath() const; QString qmlImportPath() const;
enum LogDirection { enum LogDirection {
@@ -153,6 +151,7 @@ private:
LogReceive LogReceive
}; };
void logMessage(LogDirection direction, const QString &str); void logMessage(LogDirection direction, const QString &str);
QString toFileInProject(const QString &file);
private: private:
friend class QmlCppEngine; friend class QmlCppEngine;