/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/#include "scriptdebugger.h"#include "scriptbreakpointmanager.h"#include <QtScript/QScriptEngine>#include <QtScript/QScriptEngineAgent>#include <QtScript/QScriptContextInfo>#include <QtScript/QScriptValueIterator>#include <QtCore/QTextStream>#include <QtCore/QStack>staticQString safeValueToString(constQScriptValue&value)
{
if (value.isObject())
return QLatin1String("[object Object]");
elsereturn value.toString();
}
class ScriptInfo;
class ScriptBreakpointManager;
class ScriptDebuggerPrivate
: publicQScriptEngineAgent
{
Q_DECLARE_PUBLIC(ScriptDebugger)
public:
enum Mode {
Run,
StepInto,
StepOver
};
ScriptDebuggerPrivate(QScriptEngine*engine);
~ScriptDebuggerPrivate();
// QScriptEngineAgent interfacevoid scriptLoad(qint64 id,constQString&program,constQString&fileName,int lineNumber);
void scriptUnload(qint64 id);
void positionChange(qint64 scriptId,int lineNumber,int columnNumber);
void functionEntry(qint64 scriptId);
void functionExit(qint64 scriptId,constQScriptValue&returnValue);
void exceptionThrow(qint64 scriptId,constQScriptValue&exception,bool hasHandler);
void interactive();
bool executeCommand(constQString&command,constQStringList&args);
void setMode(Mode mode);
Mode mode() const;
int frameCount() const;
void setCurrentFrameIndex(int index);
int currentFrameIndex() const;
QScriptContext*frameContext(int index) const;
QScriptContext*currentFrameContext() const;
ScriptInfo *scriptInfo(QScriptContext*context) const;
int listLineNumber() const;
void setListLineNumber(int lineNumber);
QString readLine();
void output(constQString&text);
void message(constQString&text);
void errorMessage(constQString&text);
// attributesQTextStream*m_defaultInputStream;
QTextStream*m_defaultOutputStream;
QTextStream*m_defaultErrorStream;
QTextStream*m_inputStream;
QTextStream*m_outputStream;
QTextStream*m_errorStream;
ScriptBreakpointManager *m_bpManager;
Mode m_mode;
QMap<qint64, ScriptInfo*> m_scripts;
QMap<QScriptContext*,QStack<qint64>> m_contextProgramIds;
QString m_lastInteractiveCommand;
QString m_commandPrefix;
int m_stepDepth;
int m_currentFrameIndex;
int m_listLineNumber;
ScriptDebugger *q_ptr;
};
class ScriptInfo
{
public:
ScriptInfo(constQString&code,constQString&fileName,int lineNumber)
: m_code(code), m_fileName(fileName), m_lineNumber(lineNumber)
{ }
inlineQString code() const
{ return m_code; }
inlineQString fileName() const
{ return m_fileName; }
inlineint lineNumber() const
{ return m_lineNumber; }
QString lineText(int lineNumber);
QMap<int,int> m_lineOffsets;
private:
int lineOffset(int lineNumber);
QString m_code;
QString m_fileName;
int m_lineNumber;
};
int ScriptInfo::lineOffset(int lineNumber)
{
QMap<int,int>::const_iterator it = m_lineOffsets.constFind(lineNumber);
if (it != m_lineOffsets.constEnd())
return it.value();
int offset;
it = m_lineOffsets.constFind(lineNumber -1);
if (it != m_lineOffsets.constEnd()) {
offset = it.value();
offset = m_code.indexOf(QLatin1Char('\n'), offset);
if (offset !=-1)
++offset;
m_lineOffsets.insert(lineNumber, offset);
} else {
int index;
it = m_lineOffsets.lowerBound(lineNumber);
--it;
if (it != m_lineOffsets.constBegin()) {
index = it.key();
offset = it.value();
} else {
index = m_lineNumber;
offset =0;
}
int j = index;
for ( ; j < lineNumber; ++j) {
m_lineOffsets.insert(j, offset);
offset = m_code.indexOf(QLatin1Char('\n'), offset);
if (offset ==-1)
break;
++offset;
}
m_lineOffsets.insert(j, offset);
}
return offset;
}
QString ScriptInfo::lineText(int lineNumber)
{
int startOffset = lineOffset(lineNumber);
if (startOffset ==-1)
returnQString();
int endOffset = lineOffset(lineNumber +1);
if (endOffset ==-1)
return m_code.mid(startOffset);
elsereturn m_code.mid(startOffset, endOffset - startOffset -1);
}
ScriptDebuggerPrivate::ScriptDebuggerPrivate(QScriptEngine*engine)
: QScriptEngineAgent(engine), m_mode(Run)
{
m_commandPrefix = QLatin1String(".");
m_bpManager =new ScriptBreakpointManager;
m_defaultInputStream =newQTextStream(stdin);
m_defaultOutputStream =newQTextStream(stdout);
m_defaultErrorStream =newQTextStream(stderr);
m_inputStream = m_defaultInputStream;
m_outputStream = m_defaultOutputStream;
m_errorStream = m_defaultErrorStream;
}
ScriptDebuggerPrivate::~ScriptDebuggerPrivate()
{
delete m_defaultInputStream;
delete m_defaultOutputStream;
delete m_defaultErrorStream;
delete m_bpManager;
qDeleteAll(m_scripts);
}
QString ScriptDebuggerPrivate::readLine()
{
return m_inputStream->readLine();
}
void ScriptDebuggerPrivate::output(constQString&text)
{
*m_outputStream << text;
}
void ScriptDebuggerPrivate::message(constQString&text)
{
*m_outputStream << text << endl;
m_outputStream->flush();
}
void ScriptDebuggerPrivate::errorMessage(constQString&text)
{
*m_errorStream << text << endl;
m_errorStream->flush();
}
void ScriptDebuggerPrivate::setMode(Mode mode)
{
m_mode = mode;
}
ScriptDebuggerPrivate::Mode ScriptDebuggerPrivate::mode() const
{
return m_mode;
}
QScriptContext*ScriptDebuggerPrivate::frameContext(int index) const
{
QScriptContext*ctx = engine()->currentContext();
for (int i =0; i < index; ++i) {
ctx = ctx->parentContext();
if (!ctx)
break;
}
return ctx;
}
int ScriptDebuggerPrivate::currentFrameIndex() const
{
return m_currentFrameIndex;
}
void ScriptDebuggerPrivate::setCurrentFrameIndex(int index)
{
m_currentFrameIndex = index;
m_listLineNumber =-1;
}
int ScriptDebuggerPrivate::listLineNumber() const
{
return m_listLineNumber;
}
void ScriptDebuggerPrivate::setListLineNumber(int lineNumber)
{
m_listLineNumber = lineNumber;
}
QScriptContext*ScriptDebuggerPrivate::currentFrameContext() const
{
return frameContext(currentFrameIndex());
}
int ScriptDebuggerPrivate::frameCount() const
{
int count =0;
QScriptContext*ctx = engine()->currentContext();
while (ctx) {
++count;
ctx = ctx->parentContext();
}
return count;
}
ScriptInfo *ScriptDebuggerPrivate::scriptInfo(QScriptContext*context) const
{
QStack<qint64> pids = m_contextProgramIds.value(context);
if (pids.isEmpty())
return0;
return m_scripts.value(pids.top());
}
void ScriptDebuggerPrivate::interactive()
{
setCurrentFrameIndex(0);
QString qsdbgPrompt =QString::fromLatin1("(qsdbg) ");
QString dotPrompt =QString::fromLatin1(".... ");
QString prompt = qsdbgPrompt;
QString code;
forever {
*m_outputStream << prompt;
m_outputStream->flush();
QString line = readLine();
if (code.isEmpty() && (line.isEmpty() || line.startsWith(m_commandPrefix))) {
if (line.isEmpty())
line = m_lastInteractiveCommand;
else
m_lastInteractiveCommand = line;
QStringList parts = line.split(QLatin1Char(' '),QString::SkipEmptyParts);
if (!parts.isEmpty()) {
QString command = parts.takeFirst().mid(1);
if (executeCommand(command, parts))
break;
}
} else {
if (line.isEmpty())
continue;
code += line;
code += QLatin1Char('\n');
if (line.trimmed().isEmpty()) {
continue;
} elseif (! engine()->canEvaluate(code)) {
prompt = dotPrompt;
} else {
setMode(Run);
QScriptValue result = engine()->evaluate(code, QLatin1String("typein"));
code.clear();
prompt = qsdbgPrompt;
if (! result.isUndefined()) {
errorMessage(result.toString());
engine()->clearExceptions();
}
}
}
}
}
bool ScriptDebuggerPrivate::executeCommand(constQString&command,constQStringList&args)
{
if (command == QLatin1String("c")
|| command == QLatin1String("continue")) {
setMode(Run);
returntrue;
} elseif (command == QLatin1String("s")
|| command == QLatin1String("step")) {
setMode(StepInto);
returntrue;
} elseif (command == QLatin1String("n")
|| command == QLatin1String("next")) {
setMode(StepOver);
m_stepDepth =0;
returntrue;
} elseif (command == QLatin1String("f")
|| command == QLatin1String("frame")) {
bool ok =false;
int index = args.value(0).toInt(&ok);
if (ok) {
if (index <0|| index >= frameCount()) {
errorMessage("No such frame.");
} else {
setCurrentFrameIndex(index);
QScriptContext*ctx = currentFrameContext();
message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString()));
}
}
} elseif (command == QLatin1String("bt")
|| command == QLatin1String("backtrace")) {
QScriptContext*ctx = engine()->currentContext();
int index =-1;
while (ctx) {
++index;
QString line = ctx->toString();
message(QString::fromLatin1("#%0 %1").arg(index).arg(line));
ctx = ctx->parentContext();
}
} elseif (command == QLatin1String("up")) {
int index = currentFrameIndex() +1;
if (index == frameCount()) {
errorMessage(QString::fromLatin1("Initial frame selected; you cannot go up."));
} else {
setCurrentFrameIndex(index);
QScriptContext*ctx = currentFrameContext();
message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString()));
}
} elseif (command == QLatin1String("down")) {
int index = currentFrameIndex() -1;
if (index <0) {
errorMessage(QString::fromLatin1("Bottom (innermost) frame selected; you cannot go down."));
} else {
setCurrentFrameIndex(index);
QScriptContext*ctx = currentFrameContext();
message(QString::fromLatin1("#%0 %1").arg(index).arg(ctx->toString()));
}
} elseif (command == QLatin1String("b")
|| command == QLatin1String("break")) {
QString str = args.value(0);
int colonIndex = str.indexOf(QLatin1Char(':'));
if (colonIndex !=-1) {
// filename:line formQString fileName = str.left(colonIndex);
int lineNumber = str.mid(colonIndex+1).toInt();
int id = m_bpManager->setBreakpoint(fileName, lineNumber);
message(QString::fromLatin1("Breakpoint %0 at %1, line %2.").arg(id+1).arg(fileName).arg(lineNumber));
} else {
// functionQScriptValue fun = engine()->globalObject().property(str);
if (fun.isFunction()) {
int id = m_bpManager->setBreakpoint(fun);
message(QString::fromLatin1("Breakpoint %0 at %1().").arg(id+1).arg(str));
}
}
} elseif (command == QLatin1String("d")
|| command == QLatin1String("delete")) {
int id = args.value(0).toInt() -1;
m_bpManager->removeBreakpoint(id);
} elseif (command == QLatin1String("disable")) {
int id = args.value(0).toInt() -1;
m_bpManager->setBreakpointEnabled(id,false);
} elseif (command == QLatin1String("enable")) {
int id = args.value(0).toInt() -1;
m_bpManager->setBreakpointEnabled(id,true);
} elseif (command == QLatin1String("list")) {
QScriptContext*ctx = currentFrameContext();
ScriptInfo *progInfo = scriptInfo(ctx);
if (!progInfo) {
errorMessage("No source text available for this frame.");
} else {
QScriptContextInfo ctxInfo(ctx);
bool ok;
int line = args.value(0).toInt(&ok);
if (ok) {
line =qMax(1, line -5);
} else {
line = listLineNumber();
if (line ==-1)
line =qMax(progInfo->lineNumber(), ctxInfo.lineNumber() -5);
}
for (int i = line; i < line +10; ++i) {
message(QString::fromLatin1("%0\t%1").arg(i).arg(progInfo->lineText(i)));
}
setListLineNumber(line +10);
}
} elseif (command == QLatin1String("info")) {
if (args.size() <1) {
} else {
QString what = args.value(0);
if (what == QLatin1String("locals")) {
QScriptValueIterator it(currentFrameContext()->activationObject());
while (it.hasNext()) {
it.next();
QString line;
line.append(it.name());
line.append(QLatin1String(" = "));
line.append(safeValueToString(it.value()));
message(line);
}
}
}
} elseif (command == QLatin1String("help")) {
message("continue - continue execution\n""step - step into statement\n""next - step over statement\n""list - show where you are\n""\n""break - set breakpoint\n""delete - remove breakpoint\n""disable - disable breakpoint\n""enable - enable breakpoint\n""\n""backtrace - show backtrace\n""up - one frame up\n""down - one frame down\n""frame - set frame\n""\n""info locals - show local variables");
} else {
errorMessage(QString::fromLatin1("Undefined command \"%0\". Try \"help\".")
.arg(command));
}
returnfalse;
}
// QScriptEngineAgent interfacevoid ScriptDebuggerPrivate::scriptLoad(qint64 id,constQString&program,constQString&fileName,int lineNumber)
{
ScriptInfo *info =new ScriptInfo(program, fileName, lineNumber);
m_scripts.insert(id, info);
}
void ScriptDebuggerPrivate::scriptUnload(qint64 id)
{
ScriptInfo *info = m_scripts.take(id);
delete info;
}
void ScriptDebuggerPrivate::functionEntry(qint64 scriptId)
{
if (scriptId !=-1) {
QScriptContext*ctx = engine()->currentContext();
QStack<qint64> ids = m_contextProgramIds.value(ctx);
ids.push(scriptId);
m_contextProgramIds.insert(ctx, ids);
}
if (mode() == StepOver)
++m_stepDepth;
}
void ScriptDebuggerPrivate::functionExit(qint64 scriptId,constQScriptValue&/*returnValue*/)
{
if (scriptId !=-1) {
QScriptContext*ctx = engine()->currentContext();
QStack<qint64> ids = m_contextProgramIds.value(ctx);
Q_ASSERT(!ids.isEmpty());
Q_ASSERT(ids.top() == scriptId);
ids.pop();
m_contextProgramIds.insert(ctx, ids);
}
if (mode() == StepOver)
--m_stepDepth;
}
void ScriptDebuggerPrivate::positionChange(qint64 scriptId,int lineNumber,int/*columnNumber*/)
{
ScriptInfo *info =0;
bool enterInteractiveMode =false;
if (m_bpManager->hasBreakpoints()) {
// check if we hit a breakpoint
info = m_scripts.value(scriptId);
QScriptContext*ctx = engine()->currentContext();
QScriptContextInfo ctxInfo(ctx);
QScriptValue callee = ctx->callee();
// try fileName:lineNumberint bpid = m_bpManager->findBreakpoint(info->fileName(), lineNumber);
if ((bpid !=-1) && m_bpManager->isBreakpointEnabled(bpid)) {
message(QString::fromLatin1("Breakpoint %0 at %1:%2")
.arg(bpid +1).arg(info->fileName()).arg(lineNumber));
if (m_bpManager->isBreakpointSingleShot(bpid))
m_bpManager->removeBreakpoint(bpid);
}
if (bpid ==-1) {
// try function
bpid = m_bpManager->findBreakpoint(callee);
if ((bpid !=-1) && m_bpManager->isBreakpointEnabled(bpid)) {
message(QString::fromLatin1("Breakpoint %0, %1()")
.arg(bpid +1).arg(ctxInfo.functionName()));
if (m_bpManager->isBreakpointSingleShot(bpid))
m_bpManager->removeBreakpoint(bpid);
}
}
if ((bpid ==-1) &&!ctxInfo.functionName().isEmpty()) {
// try functionName:fileName
bpid = m_bpManager->findBreakpoint(ctxInfo.functionName(), ctxInfo.fileName());
if ((bpid !=-1) && m_bpManager->isBreakpointEnabled(bpid)) {
message(QString::fromLatin1("Breakpoint %0, %1():%2").arg(bpid +1)
.arg(ctxInfo.functionName()).arg(ctxInfo.fileName()));
if (m_bpManager->isBreakpointSingleShot(bpid))
m_bpManager->removeBreakpoint(bpid);
}
}
enterInteractiveMode = (bpid !=-1);
}
switch (mode()) {
case Run:
break;
case StepInto:
enterInteractiveMode =true;
break;
case StepOver:
enterInteractiveMode = enterInteractiveMode || (m_stepDepth <=0);
break;
}
if (enterInteractiveMode) {
if (!info)
info = m_scripts.value(scriptId);
Q_ASSERT(info);
message(QString::fromLatin1("%0\t%1").arg(lineNumber).arg(info->lineText(lineNumber)));
interactive();
}
}
void ScriptDebuggerPrivate::exceptionThrow(qint64/*scriptId*/,constQScriptValue&exception,bool hasHandler)
{
if (!hasHandler) {
errorMessage(QString::fromLatin1("uncaught exception: %0").arg(exception.toString()));
QScriptContext*ctx = engine()->currentContext();
int lineNumber =QScriptContextInfo(ctx).lineNumber();
ScriptInfo *info = scriptInfo(ctx);
QString lineText = info ? info->lineText(lineNumber) : QString("(no source text available)");
message(QString::fromLatin1("%0\t%1").arg(lineNumber).arg(lineText));
interactive();
}
}
ScriptDebugger::ScriptDebugger(QScriptEngine*engine)
: d_ptr(new ScriptDebuggerPrivate(engine))
{
d_ptr->q_ptr =this;
engine->setAgent(d_ptr);
}
ScriptDebugger::ScriptDebugger(QScriptEngine*engine, ScriptDebuggerPrivate &dd)
: d_ptr(&dd)
{
d_ptr->q_ptr =this;
engine->setAgent(d_ptr);
}
ScriptDebugger::~ScriptDebugger()
{
delete d_ptr;
d_ptr =0;
}
void ScriptDebugger::breakAtNextStatement()
{
Q_D(ScriptDebugger);
d->setMode(ScriptDebuggerPrivate::StepInto);
}
void ScriptDebugger::setBreakpoint(constQString&fileName,int lineNumber)
{
Q_D(ScriptDebugger);
d->m_bpManager->setBreakpoint(fileName, lineNumber);
}
void ScriptDebugger::setBreakpoint(constQString&functionName,constQString&fileName)
{
Q_D(ScriptDebugger);
d->m_bpManager->setBreakpoint(functionName, fileName);
}
void ScriptDebugger::setBreakpoint(constQScriptValue&function)
{
Q_D(ScriptDebugger);
d->m_bpManager->setBreakpoint(function);
}
QTextStream*ScriptDebugger::inputStream() const
{
Q_D(const ScriptDebugger);
return d->m_inputStream;
}
void ScriptDebugger::setInputStream(QTextStream*inputStream)
{
Q_D(ScriptDebugger);
d->m_inputStream = inputStream;
}
QTextStream*ScriptDebugger::outputStream() const
{
Q_D(const ScriptDebugger);
return d->m_outputStream;
}
void ScriptDebugger::setOutputStream(QTextStream*outputStream)
{
Q_D(ScriptDebugger);
d->m_outputStream = outputStream;
}
QTextStream*ScriptDebugger::errorStream() const
{
Q_D(const ScriptDebugger);
return d->m_errorStream;
}
void ScriptDebugger::setErrorStream(QTextStream*errorStream)
{
Q_D(ScriptDebugger);
d->m_errorStream = errorStream;
}
Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter
ou par MP !