From 20189574f68c93276cfd856d0ed4f201eeb31db7 Mon Sep 17 00:00:00 2001 From: Kai Koehne <kai.koehne@nokia.com> Date: Fri, 25 Feb 2011 11:57:34 +0100 Subject: [PATCH] QmlJsDebugger: Handle breakpoints in qrc:// resources more gracefully Reviewed-by: Christiaan Janssen --- .../qml/qmljsdebugger/jsdebuggeragent.cpp | 32 ++-- src/plugins/debugger/qml/qmlengine.cpp | 153 ++++++++++-------- src/plugins/debugger/qml/qmlengine.h | 3 +- 3 files changed, 103 insertions(+), 85 deletions(-) diff --git a/share/qtcreator/qml/qmljsdebugger/jsdebuggeragent.cpp b/share/qtcreator/qml/qmljsdebugger/jsdebuggeragent.cpp index 3c200d797d7..7e231d91315 100644 --- a/share/qtcreator/qml/qmljsdebugger/jsdebuggeragent.cpp +++ b/share/qtcreator/qml/qmljsdebugger/jsdebuggeragent.cpp @@ -46,7 +46,6 @@ #include <QtCore/qdebug.h> #include <QtCore/qcoreapplication.h> #include <QtCore/qset.h> -#include <QtCore/qfileinfo.h> #include <QtCore/qurl.h> #include <QtScript/qscriptcontextinfo.h> #include <QtScript/qscriptengine.h> @@ -82,19 +81,19 @@ QDataStream &operator<<(QDataStream &s, const JSAgentWatchData &data) struct JSAgentStackData { QByteArray functionName; - QByteArray fileName; + QByteArray fileUrl; qint32 lineNumber; }; 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 { QByteArray functionName; - QByteArray fileName; + QByteArray fileUrl; qint32 lineNumber; }; @@ -102,22 +101,22 @@ typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; 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) { - return s >> data.functionName >> data.fileName >> data.lineNumber; + return s >> data.functionName >> data.fileUrl >> data.lineNumber; } 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) { - return b.lineNumber ^ qHash(b.fileName); + return b.lineNumber ^ qHash(b.fileUrl); } class JSDebuggerAgentPrivate @@ -316,7 +315,7 @@ void JSDebuggerAgent::scriptLoad(qint64 id, const QString &program, const QString &fileName, int) { 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); } +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) { Q_UNUSED(columnNumber); @@ -382,7 +387,7 @@ void JSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int QScriptContextInfo info(ctx); if (it == filenames.constEnd()) { // 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; frame.functionName = info.functionName().toUtf8(); @@ -392,7 +397,7 @@ void JSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, int } 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) { if (bp.lineNumber == lineNumber) { @@ -477,7 +482,7 @@ void JSDebuggerAgentPrivate::messageReceived(const QByteArray &message) fileNameToBreakpoints.clear(); foreach (const JSAgentBreakpointData &bp, breakpoints) { - fileNameToBreakpoints.insert(QFileInfo(bp.fileName).fileName(), bp); + fileNameToBreakpoints.insert(fileName(bp.fileUrl), bp); } //qDebug() << "BREAKPOINTS"; @@ -621,7 +626,8 @@ void JSDebuggerAgentPrivate::stopped() // if the line number is unknown, fallback to the function line number if (frame.lineNumber == -1) frame.lineNumber = info.functionStartLineNumber(); - frame.fileName = QUrl(info.fileName()).toLocalFile().toUtf8(); + + frame.fileUrl = info.fileName().toUtf8(); backtrace.append(frame); } QList<JSAgentWatchData> watches; diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index cb52e02aa58..a5958ac2ef0 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -57,6 +57,7 @@ #include <utils/environment.h> #include <utils/abstractprocess.h> #include <utils/qtcassert.h> +#include <utils/fileinprojectfinder.h> #include <coreplugin/icore.h> #include <coreplugin/helpmanager.h> @@ -93,31 +94,49 @@ namespace Internal { struct JSAgentBreakpointData { QByteArray functionName; - QByteArray fileName; + QByteArray fileUrl; + qint32 lineNumber; +}; + +struct JSAgentStackData +{ + QByteArray functionName; + QByteArray fileUrl; qint32 lineNumber; }; uint qHash(const JSAgentBreakpointData &b) { - return b.lineNumber ^ qHash(b.fileName); + return b.lineNumber ^ qHash(b.fileUrl); } 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) { - 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) { - return b1.lineNumber == b2.lineNumber && b1.fileName == b2.fileName; + return b1.lineNumber == b2.lineNumber && b1.fileUrl == b2.fileUrl; } typedef QSet<JSAgentBreakpointData> JSAgentBreakpoints; +typedef QList<JSAgentStackData> JSAgentStackFrames; static QDataStream &operator>>(QDataStream &s, WatchData &data) @@ -136,19 +155,6 @@ static QDataStream &operator>>(QDataStream &s, WatchData &data) 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 { public: @@ -159,6 +165,7 @@ private: int m_ping; QmlAdapter m_adapter; ApplicationLauncher m_applicationLauncher; + Utils::FileInProjectFinder fileFinder; }; QmlEnginePrivate::QmlEnginePrivate(QmlEngine *q) @@ -183,14 +190,6 @@ QmlEngine::QmlEngine(const DebuggerStartParameters &startParameters, QmlEngine::~QmlEngine() {} -void QmlEngine::gotoLocation(const Location &loc0) -{ - Location loc = loc0; - if (isShadowBuildProject()) - loc.setFileName(fromShadowBuildFilename(loc0.fileName())); - DebuggerEngine::gotoLocation(loc); -} - void QmlEngine::setupInferior() { QTC_ASSERT(state() == InferiorSetupRequested, qDebug() << state()); @@ -576,18 +575,8 @@ void QmlEngine::attemptBreakpointSynchronization() if (handler->state(id) == BreakpointInsertRequested) { 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; - bp.fileName = processedFilename.toUtf8(); + bp.fileUrl = QUrl::fromLocalFile(handler->fileName(id)).toString().toUtf8(); bp.lineNumber = handler->lineNumber(id); bp.functionName = handler->functionName(id).toUtf8(); breakpoints.insert(bp); @@ -606,7 +595,7 @@ void QmlEngine::attemptBreakpointSynchronization() QStringList breakPointsStr; foreach (const JSAgentBreakpointData &bp, breakpoints) { 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(", "))); @@ -754,6 +743,38 @@ unsigned QmlEngine::debuggerCapabilities() const | 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) { QByteArray rwData = message; @@ -769,7 +790,7 @@ void QmlEngine::messageReceived(const QByteArray &message) QString logString = QString::fromLatin1(command); - StackFrames stackFrames; + JSAgentStackFrames stackFrames; QList<WatchData> watches; QList<WatchData> 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)"). arg(stackFrames.size()).arg(watches.size()).arg(locals.size()); - for (int i = 0; i != stackFrames.size(); ++i) - stackFrames[i].level = i + 1; + StackFrames ideStackFrames; + 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>") - stackFrames.takeLast(); - stackHandler()->setFrames(stackFrames); + if (ideStackFrames.size() && ideStackFrames.back().function == "<global>") + ideStackFrames.takeLast(); + stackHandler()->setFrames(ideStackFrames); watchHandler()->beginCycle(); bool needPing = false; @@ -807,10 +836,11 @@ void QmlEngine::messageReceived(const QByteArray &message) } } - if (needPing) + if (needPing) { sendPing(); - else + } else { watchHandler()->endCycle(); + } bool becauseOfException; stream >> becauseOfException; @@ -829,7 +859,7 @@ void QmlEngine::messageReceived(const QByteArray &message) ? tr("<p>An uncaught exception occurred:</p><p>%1</p>") .arg(Qt::escape(error)) : 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); } else { // @@ -839,14 +869,10 @@ void QmlEngine::messageReceived(const QByteArray &message) QString function; int line = -1; - if (!stackFrames.isEmpty()) { - file = stackFrames.at(0).file; - line = stackFrames.at(0).line; - function = stackFrames.at(0).function; - - if (isShadowBuildProject()) { - file = fromShadowBuildFilename(file); - } + if (!ideStackFrames.isEmpty()) { + file = ideStackFrames.at(0).file; + line = ideStackFrames.at(0).line; + function = ideStackFrames.at(0).function; } BreakHandler *handler = breakHandler(); @@ -865,8 +891,8 @@ void QmlEngine::messageReceived(const QByteArray &message) logMessage(LogReceive, logString); } - if (!stackFrames.isEmpty()) - gotoLocation(stackFrames.value(0)); + if (!ideStackFrames.isEmpty()) + gotoLocation(ideStackFrames.value(0)); } else if (command == "RESULT") { WatchData data; @@ -977,19 +1003,6 @@ QString QmlEngine::qmlImportPath() const 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, const QString &oldBasePath, const QString &newBasePath) const { diff --git a/src/plugins/debugger/qml/qmlengine.h b/src/plugins/debugger/qml/qmlengine.h index d230a927a4e..b9c23fca116 100644 --- a/src/plugins/debugger/qml/qmlengine.h +++ b/src/plugins/debugger/qml/qmlengine.h @@ -58,7 +58,6 @@ public: void handleRemoteSetupDone(int gdbServerPort, int qmlPort); void handleRemoteSetupFailed(const QString &message); - void gotoLocation(const Location &location); bool canDisplayTooltip() const; void showMessage(const QString &msg, int channel = LogDebug, @@ -145,7 +144,6 @@ private: QString fromShadowBuildFilename(const QString &filename) const; QString mangleFilenamePaths(const QString &filename, const QString &oldBasePath, const QString &newBasePath) const; - QString toShadowBuildFilename(const QString &filename) const; QString qmlImportPath() const; enum LogDirection { @@ -153,6 +151,7 @@ private: LogReceive }; void logMessage(LogDirection direction, const QString &str); + QString toFileInProject(const QString &file); private: friend class QmlCppEngine; -- GitLab