forked from qt-creator/qt-creator
QmlJsDebugger: Handle breakpoints in qrc:// resources more gracefully
Reviewed-by: Christiaan Janssen
This commit is contained in:
@@ -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;
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user