You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1140 lines
33 KiB
1140 lines
33 KiB
15 years ago
|
/*
|
||
|
* This file is part of the KDE libraries
|
||
|
* Copyright (C) 2000-2001 Harri Porten (porten@kde.org)
|
||
|
* Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public
|
||
|
* License along with this library; if not, write to the Free Software
|
||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
|
*/
|
||
|
|
||
|
#include "kjs_debugwin.h"
|
||
|
#include "kjs_proxy.h"
|
||
|
|
||
|
#ifdef KJS_DEBUGGER
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <qlayout.h>
|
||
|
#include <qpushbutton.h>
|
||
|
#include <qtextedit.h>
|
||
|
#include <qlistbox.h>
|
||
|
#include <qmultilineedit.h>
|
||
|
#include <qapplication.h>
|
||
|
#include <qsplitter.h>
|
||
|
#include <qcombobox.h>
|
||
|
#include <qbitmap.h>
|
||
|
#include <qwidgetlist.h>
|
||
|
#include <qlabel.h>
|
||
|
#include <qdatastream.h>
|
||
|
#include <qcstring.h>
|
||
|
#include <qpainter.h>
|
||
|
#include <qscrollbar.h>
|
||
|
|
||
|
#include <klocale.h>
|
||
|
#include <kdebug.h>
|
||
|
#include <kiconloader.h>
|
||
|
#include <kglobal.h>
|
||
|
#include <kmessagebox.h>
|
||
|
#include <kguiitem.h>
|
||
|
#include <kpopupmenu.h>
|
||
|
#include <kmenubar.h>
|
||
|
#include <kaction.h>
|
||
|
#include <kactioncollection.h>
|
||
|
#include <kglobalsettings.h>
|
||
|
#include <kshortcut.h>
|
||
|
#include <kconfig.h>
|
||
|
#include <kconfigbase.h>
|
||
|
#include <kapplication.h>
|
||
|
#include <dcop/dcopclient.h>
|
||
|
#include <kstringhandler.h>
|
||
|
|
||
|
#include "kjs_dom.h"
|
||
|
#include "kjs_binding.h"
|
||
|
#include "khtml_part.h"
|
||
|
#include "khtmlview.h"
|
||
|
#include "khtml_pagecache.h"
|
||
|
#include "khtml_settings.h"
|
||
|
#include "khtml_factory.h"
|
||
|
#include "misc/decoder.h"
|
||
|
#include <kjs/ustring.h>
|
||
|
#include <kjs/object.h>
|
||
|
#include <kjs/function.h>
|
||
|
#include <kjs/interpreter.h>
|
||
|
|
||
|
using namespace KJS;
|
||
|
using namespace khtml;
|
||
|
|
||
|
SourceDisplay::SourceDisplay(KJSDebugWin *debugWin, QWidget *parent, const char *name)
|
||
|
: QScrollView(parent,name), m_currentLine(-1), m_sourceFile(0), m_debugWin(debugWin),
|
||
|
m_font(KGlobalSettings::fixedFont())
|
||
|
{
|
||
|
verticalScrollBar()->setLineStep(QFontMetrics(m_font).height());
|
||
|
viewport()->setBackgroundMode(Qt::NoBackground);
|
||
|
m_breakpointIcon = KGlobal::iconLoader()->loadIcon("stop",KIcon::Small);
|
||
|
}
|
||
|
|
||
|
SourceDisplay::~SourceDisplay()
|
||
|
{
|
||
|
if (m_sourceFile) {
|
||
|
m_sourceFile->deref();
|
||
|
m_sourceFile = 0L;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SourceDisplay::setSource(SourceFile *sourceFile)
|
||
|
{
|
||
|
if ( sourceFile )
|
||
|
sourceFile->ref();
|
||
|
if (m_sourceFile)
|
||
|
m_sourceFile->deref();
|
||
|
m_sourceFile = sourceFile;
|
||
|
if ( m_sourceFile )
|
||
|
m_sourceFile->ref();
|
||
|
|
||
|
if (!m_sourceFile || !m_debugWin->isVisible()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QString code = sourceFile->getCode();
|
||
|
const QChar *chars = code.unicode();
|
||
|
uint len = code.length();
|
||
|
QChar newLine('\n');
|
||
|
QChar cr('\r');
|
||
|
QChar tab('\t');
|
||
|
QString tabstr(" ");
|
||
|
QString line;
|
||
|
m_lines.clear();
|
||
|
int width = 0;
|
||
|
QFontMetrics metrics(m_font);
|
||
|
|
||
|
for (uint pos = 0; pos < len; pos++) {
|
||
|
QChar c = chars[pos];
|
||
|
if (c == cr) {
|
||
|
if (pos < len-1 && chars[pos+1] == newLine)
|
||
|
continue;
|
||
|
else
|
||
|
c = newLine;
|
||
|
}
|
||
|
if (c == newLine) {
|
||
|
m_lines.append(line);
|
||
|
int lineWidth = metrics.width(line);
|
||
|
if (lineWidth > width)
|
||
|
width = lineWidth;
|
||
|
line = "";
|
||
|
}
|
||
|
else if (c == tab) {
|
||
|
line += tabstr;
|
||
|
}
|
||
|
else {
|
||
|
line += c;
|
||
|
}
|
||
|
}
|
||
|
if (line.length()) {
|
||
|
m_lines.append(line);
|
||
|
int lineWidth = metrics.width(line);
|
||
|
if (lineWidth > width)
|
||
|
width = lineWidth;
|
||
|
}
|
||
|
|
||
|
int linenoDisplayWidth = metrics.width("888888");
|
||
|
resizeContents(linenoDisplayWidth+4+width,metrics.height()*m_lines.count());
|
||
|
update();
|
||
|
sourceFile->deref();
|
||
|
}
|
||
|
|
||
|
void SourceDisplay::setCurrentLine(int lineno, bool doCenter)
|
||
|
{
|
||
|
m_currentLine = lineno;
|
||
|
|
||
|
if (doCenter && m_currentLine >= 0) {
|
||
|
QFontMetrics metrics(m_font);
|
||
|
int height = metrics.height();
|
||
|
center(0,height*m_currentLine+height/2);
|
||
|
}
|
||
|
|
||
|
updateContents();
|
||
|
}
|
||
|
|
||
|
void SourceDisplay::contentsMousePressEvent(QMouseEvent *e)
|
||
|
{
|
||
|
QScrollView::mouseDoubleClickEvent(e);
|
||
|
QFontMetrics metrics(m_font);
|
||
|
int lineno = e->y()/metrics.height();
|
||
|
emit lineDoubleClicked(lineno+1); // line numbers start from 1
|
||
|
}
|
||
|
|
||
|
void SourceDisplay::showEvent(QShowEvent *)
|
||
|
{
|
||
|
setSource(m_sourceFile);
|
||
|
}
|
||
|
|
||
|
void SourceDisplay::drawContents(QPainter *p, int clipx, int clipy, int clipw, int cliph)
|
||
|
{
|
||
|
if (!m_sourceFile) {
|
||
|
p->fillRect(clipx,clipy,clipw,cliph,palette().active().base());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QFontMetrics metrics(m_font);
|
||
|
int height = metrics.height();
|
||
|
|
||
|
int bottom = clipy + cliph;
|
||
|
int right = clipx + clipw;
|
||
|
|
||
|
int firstLine = clipy/height-1;
|
||
|
if (firstLine < 0)
|
||
|
firstLine = 0;
|
||
|
int lastLine = bottom/height+2;
|
||
|
if (lastLine > (int)m_lines.count())
|
||
|
lastLine = m_lines.count();
|
||
|
|
||
|
p->setFont(m_font);
|
||
|
|
||
|
int linenoWidth = metrics.width("888888");
|
||
|
|
||
|
for (int lineno = firstLine; lineno <= lastLine; lineno++) {
|
||
|
QString linenoStr = QString().sprintf("%d",lineno+1);
|
||
|
|
||
|
|
||
|
p->fillRect(0,height*lineno,linenoWidth,height,palette().active().mid());
|
||
|
|
||
|
p->setPen(palette().active().text());
|
||
|
p->drawText(0,height*lineno,linenoWidth,height,Qt::AlignRight,linenoStr);
|
||
|
|
||
|
QColor bgColor;
|
||
|
QColor textColor;
|
||
|
|
||
|
if (lineno == m_currentLine) {
|
||
|
bgColor = palette().active().highlight();
|
||
|
textColor = palette().active().highlightedText();
|
||
|
}
|
||
|
else if (m_debugWin->haveBreakpoint(m_sourceFile,lineno+1,lineno+1)) {
|
||
|
bgColor = palette().active().text();
|
||
|
textColor = palette().active().base();
|
||
|
p->drawPixmap(2,height*lineno+height/2-m_breakpointIcon.height()/2,m_breakpointIcon);
|
||
|
}
|
||
|
else {
|
||
|
bgColor = palette().active().base();
|
||
|
textColor = palette().active().text();
|
||
|
}
|
||
|
|
||
|
p->fillRect(linenoWidth,height*lineno,right-linenoWidth,height,bgColor);
|
||
|
p->setPen(textColor);
|
||
|
p->drawText(linenoWidth+4,height*lineno,contentsWidth()-linenoWidth-4,height,
|
||
|
Qt::AlignLeft,m_lines[lineno]);
|
||
|
}
|
||
|
|
||
|
int remainingTop = height*(lastLine+1);
|
||
|
p->fillRect(0,remainingTop,linenoWidth,bottom-remainingTop,palette().active().mid());
|
||
|
|
||
|
p->fillRect(linenoWidth,remainingTop,
|
||
|
right-linenoWidth,bottom-remainingTop,palette().active().base());
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
KJSDebugWin * KJSDebugWin::kjs_html_debugger = 0;
|
||
|
|
||
|
QString SourceFile::getCode()
|
||
|
{
|
||
|
if (interpreter) {
|
||
|
KHTMLPart *part = ::qt_cast<KHTMLPart*>(static_cast<ScriptInterpreter*>(interpreter)->part());
|
||
|
if (part && url == part->url().url() && KHTMLPageCache::self()->isValid(part->cacheId())) {
|
||
|
Decoder *decoder = part->createDecoder();
|
||
|
QByteArray data;
|
||
|
QDataStream stream(data,IO_WriteOnly);
|
||
|
KHTMLPageCache::self()->saveData(part->cacheId(),&stream);
|
||
|
QString str;
|
||
|
if (data.size() == 0)
|
||
|
str = "";
|
||
|
else
|
||
|
str = decoder->decode(data.data(),data.size()) + decoder->flush();
|
||
|
delete decoder;
|
||
|
return str;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return code;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
SourceFragment::SourceFragment(int sid, int bl, int el, SourceFile *sf)
|
||
|
{
|
||
|
sourceId = sid;
|
||
|
baseLine = bl;
|
||
|
errorLine = el;
|
||
|
sourceFile = sf;
|
||
|
sourceFile->ref();
|
||
|
}
|
||
|
|
||
|
SourceFragment::~SourceFragment()
|
||
|
{
|
||
|
sourceFile->deref();
|
||
|
sourceFile = 0L;
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
|
||
|
KJSErrorDialog::KJSErrorDialog(QWidget *parent, const QString& errorMessage, bool showDebug)
|
||
|
: KDialogBase(parent,0,true,i18n("JavaScript Error"),
|
||
|
showDebug ? KDialogBase::Ok|KDialogBase::User1 : KDialogBase::Ok,
|
||
|
KDialogBase::Ok,false,KGuiItem("&Debug","gear"))
|
||
|
{
|
||
|
QWidget *page = new QWidget(this);
|
||
|
setMainWidget(page);
|
||
|
|
||
|
QLabel *iconLabel = new QLabel("",page);
|
||
|
iconLabel->setPixmap(KGlobal::iconLoader()->loadIcon("messagebox_critical",
|
||
|
KIcon::NoGroup,KIcon::SizeMedium,
|
||
|
KIcon::DefaultState,0,true));
|
||
|
|
||
|
QWidget *contents = new QWidget(page);
|
||
|
QLabel *label = new QLabel(errorMessage,contents);
|
||
|
m_dontShowAgainCb = new QCheckBox(i18n("&Do not show this message again"),contents);
|
||
|
|
||
|
QVBoxLayout *vl = new QVBoxLayout(contents,0,spacingHint());
|
||
|
vl->addWidget(label);
|
||
|
vl->addWidget(m_dontShowAgainCb);
|
||
|
|
||
|
QHBoxLayout *topLayout = new QHBoxLayout(page,0,spacingHint());
|
||
|
topLayout->addWidget(iconLabel);
|
||
|
topLayout->addWidget(contents);
|
||
|
topLayout->addStretch(10);
|
||
|
|
||
|
m_debugSelected = false;
|
||
|
}
|
||
|
|
||
|
KJSErrorDialog::~KJSErrorDialog()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void KJSErrorDialog::slotUser1()
|
||
|
{
|
||
|
m_debugSelected = true;
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
EvalMultiLineEdit::EvalMultiLineEdit(QWidget *parent)
|
||
|
: QMultiLineEdit(parent) {
|
||
|
}
|
||
|
|
||
|
void EvalMultiLineEdit::keyPressEvent(QKeyEvent * e)
|
||
|
{
|
||
|
if (e->key() == Qt::Key_Return) {
|
||
|
if (hasSelectedText()) {
|
||
|
m_code = selectedText();
|
||
|
} else {
|
||
|
int para, index;
|
||
|
getCursorPosition(¶, &index);
|
||
|
m_code = text(para);
|
||
|
}
|
||
|
end();
|
||
|
}
|
||
|
QMultiLineEdit::keyPressEvent(e);
|
||
|
}
|
||
|
//-------------------------------------------------------------------------
|
||
|
KJSDebugWin::KJSDebugWin(QWidget *parent, const char *name)
|
||
|
: KMainWindow(parent, name, WType_TopLevel), KInstance("kjs_debugger")
|
||
|
{
|
||
|
m_breakpoints = 0;
|
||
|
m_breakpointCount = 0;
|
||
|
|
||
|
m_curSourceFile = 0;
|
||
|
m_mode = Continue;
|
||
|
m_nextSourceUrl = "";
|
||
|
m_nextSourceBaseLine = 1;
|
||
|
m_execs = 0;
|
||
|
m_execsCount = 0;
|
||
|
m_execsAlloc = 0;
|
||
|
m_steppingDepth = 0;
|
||
|
|
||
|
m_stopIcon = KGlobal::iconLoader()->loadIcon("stop",KIcon::Small);
|
||
|
m_emptyIcon = QPixmap(m_stopIcon.width(),m_stopIcon.height());
|
||
|
QBitmap emptyMask(m_stopIcon.width(),m_stopIcon.height(),true);
|
||
|
m_emptyIcon.setMask(emptyMask);
|
||
|
|
||
|
setCaption(i18n("JavaScript Debugger"));
|
||
|
|
||
|
QWidget *mainWidget = new QWidget(this);
|
||
|
setCentralWidget(mainWidget);
|
||
|
|
||
|
QVBoxLayout *vl = new QVBoxLayout(mainWidget,5);
|
||
|
|
||
|
// frame list & code
|
||
|
QSplitter *hsplitter = new QSplitter(Qt::Vertical,mainWidget);
|
||
|
QSplitter *vsplitter = new QSplitter(hsplitter);
|
||
|
QFont font(KGlobalSettings::fixedFont());
|
||
|
|
||
|
QWidget *contextContainer = new QWidget(vsplitter);
|
||
|
|
||
|
QLabel *contextLabel = new QLabel(i18n("Call stack"),contextContainer);
|
||
|
QWidget *contextListContainer = new QWidget(contextContainer);
|
||
|
m_contextList = new QListBox(contextListContainer);
|
||
|
m_contextList->setMinimumSize(100,200);
|
||
|
connect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
|
||
|
|
||
|
QHBoxLayout *clistLayout = new QHBoxLayout(contextListContainer);
|
||
|
clistLayout->addWidget(m_contextList);
|
||
|
clistLayout->addSpacing(KDialog::spacingHint());
|
||
|
|
||
|
QVBoxLayout *contextLayout = new QVBoxLayout(contextContainer);
|
||
|
contextLayout->addWidget(contextLabel);
|
||
|
contextLayout->addSpacing(KDialog::spacingHint());
|
||
|
contextLayout->addWidget(contextListContainer);
|
||
|
|
||
|
// source selection & display
|
||
|
QWidget *sourceSelDisplay = new QWidget(vsplitter);
|
||
|
QVBoxLayout *ssdvl = new QVBoxLayout(sourceSelDisplay);
|
||
|
|
||
|
m_sourceSel = new QComboBox(toolBar());
|
||
|
connect(m_sourceSel,SIGNAL(activated(int)),this,SLOT(slotSourceSelected(int)));
|
||
|
|
||
|
m_sourceDisplay = new SourceDisplay(this,sourceSelDisplay);
|
||
|
ssdvl->addWidget(m_sourceDisplay);
|
||
|
connect(m_sourceDisplay,SIGNAL(lineDoubleClicked(int)),SLOT(slotToggleBreakpoint(int)));
|
||
|
|
||
|
QValueList<int> vsplitSizes;
|
||
|
vsplitSizes.insert(vsplitSizes.end(),120);
|
||
|
vsplitSizes.insert(vsplitSizes.end(),480);
|
||
|
vsplitter->setSizes(vsplitSizes);
|
||
|
|
||
|
// evaluate
|
||
|
|
||
|
QWidget *evalContainer = new QWidget(hsplitter);
|
||
|
|
||
|
QLabel *evalLabel = new QLabel(i18n("JavaScript console"),evalContainer);
|
||
|
m_evalEdit = new EvalMultiLineEdit(evalContainer);
|
||
|
m_evalEdit->setWordWrap(QMultiLineEdit::NoWrap);
|
||
|
m_evalEdit->setFont(font);
|
||
|
connect(m_evalEdit,SIGNAL(returnPressed()),SLOT(slotEval()));
|
||
|
m_evalDepth = 0;
|
||
|
|
||
|
QVBoxLayout *evalLayout = new QVBoxLayout(evalContainer);
|
||
|
evalLayout->addSpacing(KDialog::spacingHint());
|
||
|
evalLayout->addWidget(evalLabel);
|
||
|
evalLayout->addSpacing(KDialog::spacingHint());
|
||
|
evalLayout->addWidget(m_evalEdit);
|
||
|
|
||
|
QValueList<int> hsplitSizes;
|
||
|
hsplitSizes.insert(hsplitSizes.end(),400);
|
||
|
hsplitSizes.insert(hsplitSizes.end(),200);
|
||
|
hsplitter->setSizes(hsplitSizes);
|
||
|
|
||
|
vl->addWidget(hsplitter);
|
||
|
|
||
|
// actions
|
||
|
KPopupMenu *debugMenu = new KPopupMenu(this);
|
||
|
menuBar()->insertItem("&Debug",debugMenu);
|
||
|
|
||
|
m_actionCollection = new KActionCollection(this);
|
||
|
m_actionCollection->setInstance(this);
|
||
|
|
||
|
// Venkman use F12, KDevelop F10
|
||
|
KShortcut scNext = KShortcut(KKeySequence(KKey(Qt::Key_F12)));
|
||
|
scNext.append(KKeySequence(KKey(Qt::Key_F10)));
|
||
|
m_nextAction = new KAction(i18n("Next breakpoint","&Next"),"dbgnext",scNext,this,SLOT(slotNext()),
|
||
|
m_actionCollection,"next");
|
||
|
m_stepAction = new KAction(i18n("&Step"),"dbgstep",KShortcut(Qt::Key_F11),this,SLOT(slotStep()),
|
||
|
m_actionCollection,"step");
|
||
|
// Venkman use F5, Kdevelop F9
|
||
|
KShortcut scCont = KShortcut(KKeySequence(KKey(Qt::Key_F5)));
|
||
|
scCont.append(KKeySequence(KKey(Qt::Key_F9)));
|
||
|
m_continueAction = new KAction(i18n("&Continue"),"dbgrun",scCont,this,SLOT(slotContinue()),
|
||
|
m_actionCollection,"cont");
|
||
|
m_stopAction = new KAction(i18n("St&op"),"stop",KShortcut(Qt::Key_F4),this,SLOT(slotStop()),
|
||
|
m_actionCollection,"stop");
|
||
|
m_breakAction = new KAction(i18n("&Break at Next Statement"),"dbgrunto",KShortcut(Qt::Key_F8),this,SLOT(slotBreakNext()),
|
||
|
m_actionCollection,"breaknext");
|
||
|
|
||
|
|
||
|
m_nextAction->setToolTip(i18n("Next breakpoint","Next"));
|
||
|
m_stepAction->setToolTip(i18n("Step"));
|
||
|
m_continueAction->setToolTip(i18n("Continue"));
|
||
|
m_stopAction->setToolTip(i18n("Stop"));
|
||
|
m_breakAction->setToolTip("Break at next Statement");
|
||
|
|
||
|
m_nextAction->setEnabled(false);
|
||
|
m_stepAction->setEnabled(false);
|
||
|
m_continueAction->setEnabled(false);
|
||
|
m_stopAction->setEnabled(false);
|
||
|
m_breakAction->setEnabled(true);
|
||
|
|
||
|
m_nextAction->plug(debugMenu);
|
||
|
m_stepAction->plug(debugMenu);
|
||
|
m_continueAction->plug(debugMenu);
|
||
|
// m_stopAction->plug(debugMenu); ### disabled until DebuggerImp::stop() works reliably
|
||
|
m_breakAction->plug(debugMenu);
|
||
|
|
||
|
m_nextAction->plug(toolBar());
|
||
|
m_stepAction->plug(toolBar());
|
||
|
m_continueAction->plug(toolBar());
|
||
|
// m_stopAction->plug(toolBar()); ###
|
||
|
m_breakAction->plug(toolBar());
|
||
|
|
||
|
toolBar()->insertWidget(1,300,m_sourceSel);
|
||
|
toolBar()->setItemAutoSized(1);
|
||
|
|
||
|
updateContextList();
|
||
|
setMinimumSize(300,200);
|
||
|
resize(600,450);
|
||
|
|
||
|
}
|
||
|
|
||
|
KJSDebugWin::~KJSDebugWin()
|
||
|
{
|
||
|
free(m_breakpoints);
|
||
|
free(m_execs);
|
||
|
}
|
||
|
|
||
|
KJSDebugWin *KJSDebugWin::createInstance()
|
||
|
{
|
||
|
assert(!kjs_html_debugger);
|
||
|
kjs_html_debugger = new KJSDebugWin();
|
||
|
return kjs_html_debugger;
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::destroyInstance()
|
||
|
{
|
||
|
assert(kjs_html_debugger);
|
||
|
kjs_html_debugger->hide();
|
||
|
delete kjs_html_debugger;
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotNext()
|
||
|
{
|
||
|
m_mode = Next;
|
||
|
leaveSession();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotStep()
|
||
|
{
|
||
|
m_mode = Step;
|
||
|
leaveSession();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotContinue()
|
||
|
{
|
||
|
m_mode = Continue;
|
||
|
leaveSession();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotStop()
|
||
|
{
|
||
|
m_mode = Stop;
|
||
|
while (!m_execStates.isEmpty())
|
||
|
leaveSession();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotBreakNext()
|
||
|
{
|
||
|
m_mode = Step;
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotToggleBreakpoint(int lineno)
|
||
|
{
|
||
|
if (m_sourceSel->currentItem() < 0)
|
||
|
return;
|
||
|
|
||
|
SourceFile *sourceFile = m_sourceSelFiles.at(m_sourceSel->currentItem());
|
||
|
|
||
|
// Find the source fragment containing the selected line (if any)
|
||
|
int sourceId = -1;
|
||
|
int highestBaseLine = -1;
|
||
|
QMap<int,SourceFragment*>::Iterator it;
|
||
|
|
||
|
for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it) {
|
||
|
SourceFragment *sourceFragment = it.data();
|
||
|
if (sourceFragment &&
|
||
|
sourceFragment->sourceFile == sourceFile &&
|
||
|
sourceFragment->baseLine <= lineno &&
|
||
|
sourceFragment->baseLine > highestBaseLine) {
|
||
|
|
||
|
sourceId = sourceFragment->sourceId;
|
||
|
highestBaseLine = sourceFragment->baseLine;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sourceId < 0)
|
||
|
return;
|
||
|
|
||
|
// Update the source code display with the appropriate icon
|
||
|
int fragmentLineno = lineno-highestBaseLine+1;
|
||
|
if (!setBreakpoint(sourceId,fragmentLineno)) // was already set
|
||
|
deleteBreakpoint(sourceId,fragmentLineno);
|
||
|
|
||
|
m_sourceDisplay->updateContents();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotShowFrame(int frameno)
|
||
|
{
|
||
|
if (frameno < 0 || frameno >= m_execsCount)
|
||
|
return;
|
||
|
|
||
|
Context ctx = m_execs[frameno]->context();
|
||
|
setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotSourceSelected(int sourceSelIndex)
|
||
|
{
|
||
|
// A source file has been selected from the drop-down list - display the file
|
||
|
if (sourceSelIndex < 0 || sourceSelIndex >= (int)m_sourceSel->count())
|
||
|
return;
|
||
|
SourceFile *sourceFile = m_sourceSelFiles.at(sourceSelIndex);
|
||
|
displaySourceFile(sourceFile,true);
|
||
|
|
||
|
// If the currently selected context is in the current source file, then hilight
|
||
|
// the line it's on.
|
||
|
if (m_contextList->currentItem() >= 0) {
|
||
|
Context ctx = m_execs[m_contextList->currentItem()]->context();
|
||
|
if (m_sourceFragments[ctx.sourceId()]->sourceFile == m_sourceSelFiles.at(sourceSelIndex))
|
||
|
setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::slotEval()
|
||
|
{
|
||
|
// Work out which execution state to use. If we're currently in a debugging session,
|
||
|
// use the current context - otherwise, use the global execution state from the interpreter
|
||
|
// corresponding to the currently displayed source file.
|
||
|
ExecState *exec;
|
||
|
Object thisobj;
|
||
|
if (m_execStates.isEmpty()) {
|
||
|
if (m_sourceSel->currentItem() < 0)
|
||
|
return;
|
||
|
SourceFile *sourceFile = m_sourceSelFiles.at(m_sourceSel->currentItem());
|
||
|
if (!sourceFile->interpreter)
|
||
|
return;
|
||
|
exec = sourceFile->interpreter->globalExec();
|
||
|
thisobj = exec->interpreter()->globalObject();
|
||
|
}
|
||
|
else {
|
||
|
exec = m_execStates.top();
|
||
|
thisobj = exec->context().thisValue();
|
||
|
}
|
||
|
|
||
|
// Evaluate the js code from m_evalEdit
|
||
|
UString code(m_evalEdit->code());
|
||
|
QString msg;
|
||
|
|
||
|
KJSCPUGuard guard;
|
||
|
guard.start();
|
||
|
|
||
|
Interpreter *interp = exec->interpreter();
|
||
|
|
||
|
Object obj = Object::dynamicCast(interp->globalObject().get(exec, "eval"));
|
||
|
List args;
|
||
|
args.append(String(code));
|
||
|
|
||
|
m_evalDepth++;
|
||
|
Value retval = obj.call(exec, thisobj, args);
|
||
|
m_evalDepth--;
|
||
|
guard.stop();
|
||
|
|
||
|
// Print the return value or exception message to the console
|
||
|
if (exec->hadException()) {
|
||
|
Value exc = exec->exception();
|
||
|
exec->clearException();
|
||
|
msg = "Exception: " + exc.toString(interp->globalExec()).qstring();
|
||
|
}
|
||
|
else {
|
||
|
msg = retval.toString(interp->globalExec()).qstring();
|
||
|
}
|
||
|
|
||
|
m_evalEdit->insert(msg+"\n");
|
||
|
updateContextList();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::closeEvent(QCloseEvent *e)
|
||
|
{
|
||
|
while (!m_execStates.isEmpty()) // ### not sure if this will work
|
||
|
leaveSession();
|
||
|
return QWidget::closeEvent(e);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::eventFilter(QObject *o, QEvent *e)
|
||
|
{
|
||
|
switch (e->type()) {
|
||
|
case QEvent::MouseButtonPress:
|
||
|
case QEvent::MouseButtonRelease:
|
||
|
case QEvent::MouseButtonDblClick:
|
||
|
case QEvent::MouseMove:
|
||
|
case QEvent::KeyPress:
|
||
|
case QEvent::KeyRelease:
|
||
|
case QEvent::Destroy:
|
||
|
case QEvent::Close:
|
||
|
case QEvent::Quit:
|
||
|
while (o->parent())
|
||
|
o = o->parent();
|
||
|
if (o == this)
|
||
|
return QWidget::eventFilter(o,e);
|
||
|
else
|
||
|
return true;
|
||
|
break;
|
||
|
default:
|
||
|
return QWidget::eventFilter(o,e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::disableOtherWindows()
|
||
|
{
|
||
|
QWidgetList *widgets = QApplication::allWidgets();
|
||
|
QWidgetListIt it(*widgets);
|
||
|
for (; it.current(); ++it)
|
||
|
it.current()->installEventFilter(this);
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::enableOtherWindows()
|
||
|
{
|
||
|
QWidgetList *widgets = QApplication::allWidgets();
|
||
|
QWidgetListIt it(*widgets);
|
||
|
for (; it.current(); ++it)
|
||
|
it.current()->removeEventFilter(this);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::sourceParsed(KJS::ExecState *exec, int sourceId,
|
||
|
const KJS::UString &source, int errorLine)
|
||
|
{
|
||
|
// Work out which source file this fragment is in
|
||
|
SourceFile *sourceFile = 0;
|
||
|
if (!m_nextSourceUrl.isEmpty())
|
||
|
sourceFile = getSourceFile(exec->interpreter(),m_nextSourceUrl);
|
||
|
|
||
|
int index;
|
||
|
if (!sourceFile) {
|
||
|
index = m_sourceSel->count();
|
||
|
if (!m_nextSourceUrl.isEmpty()) {
|
||
|
|
||
|
QString code = source.qstring();
|
||
|
KParts::ReadOnlyPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
|
||
|
if (m_nextSourceUrl == part->url().url()) {
|
||
|
// Only store the code here if it's not from the part's html page... in that
|
||
|
// case we can get it from KHTMLPageCache
|
||
|
code = QString::null;
|
||
|
}
|
||
|
|
||
|
sourceFile = new SourceFile(m_nextSourceUrl,code,exec->interpreter());
|
||
|
setSourceFile(exec->interpreter(),m_nextSourceUrl,sourceFile);
|
||
|
m_sourceSelFiles.append(sourceFile);
|
||
|
m_sourceSel->insertItem(m_nextSourceUrl);
|
||
|
}
|
||
|
else {
|
||
|
// Sourced passed from somewhere else (possibly an eval call)... we don't know the url,
|
||
|
// but we still know the interpreter
|
||
|
sourceFile = new SourceFile("(unknown)",source.qstring(),exec->interpreter());
|
||
|
m_sourceSelFiles.append(sourceFile);
|
||
|
m_sourceSel->insertItem(QString::number(index) += "-???");
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// Ensure that each source file to be displayed is associated with
|
||
|
// an appropriate interpreter
|
||
|
if (!sourceFile->interpreter)
|
||
|
sourceFile->interpreter = exec->interpreter();
|
||
|
for (index = 0; index < m_sourceSel->count(); index++) {
|
||
|
if (m_sourceSelFiles.at(index) == sourceFile)
|
||
|
break;
|
||
|
}
|
||
|
assert(index < m_sourceSel->count());
|
||
|
}
|
||
|
|
||
|
SourceFragment *sf = new SourceFragment(sourceId,m_nextSourceBaseLine,errorLine,sourceFile);
|
||
|
m_sourceFragments[sourceId] = sf;
|
||
|
|
||
|
if (m_sourceSel->currentItem() < 0)
|
||
|
m_sourceSel->setCurrentItem(index);
|
||
|
|
||
|
if (m_sourceSel->currentItem() == index) {
|
||
|
displaySourceFile(sourceFile,true);
|
||
|
}
|
||
|
|
||
|
m_nextSourceBaseLine = 1;
|
||
|
m_nextSourceUrl = "";
|
||
|
|
||
|
return (m_mode != Stop);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::sourceUnused(KJS::ExecState *exec, int sourceId)
|
||
|
{
|
||
|
// Verify that there aren't any contexts on the stack using the given sourceId
|
||
|
// This should never be the case because this function is only called when
|
||
|
// the interpreter has deleted all Node objects for the source.
|
||
|
for (int e = 0; e < m_execsCount; e++)
|
||
|
assert(m_execs[e]->context().sourceId() != sourceId);
|
||
|
|
||
|
// Now remove the fragment (and the SourceFile, if it was the last fragment in that file)
|
||
|
SourceFragment *fragment = m_sourceFragments[sourceId];
|
||
|
if (fragment) {
|
||
|
m_sourceFragments.erase(sourceId);
|
||
|
|
||
|
SourceFile *sourceFile = fragment->sourceFile;
|
||
|
if (sourceFile->hasOneRef()) {
|
||
|
for (int i = 0; i < m_sourceSel->count(); i++) {
|
||
|
if (m_sourceSelFiles.at(i) == sourceFile) {
|
||
|
m_sourceSel->removeItem(i);
|
||
|
m_sourceSelFiles.remove(i);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
removeSourceFile(exec->interpreter(),sourceFile->url);
|
||
|
}
|
||
|
delete fragment;
|
||
|
}
|
||
|
|
||
|
return (m_mode != Stop);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::exception(ExecState *exec, const Value &value, bool inTryCatch)
|
||
|
{
|
||
|
assert(value.isValid());
|
||
|
|
||
|
// Ignore exceptions that will be caught by the script
|
||
|
if (inTryCatch)
|
||
|
return true;
|
||
|
|
||
|
KParts::ReadOnlyPart *part = static_cast<ScriptInterpreter*>(exec->interpreter())->part();
|
||
|
KHTMLPart *khtmlpart = ::qt_cast<KHTMLPart*>(part);
|
||
|
if (khtmlpart && !khtmlpart->settings()->isJavaScriptErrorReportingEnabled())
|
||
|
return true;
|
||
|
|
||
|
QWidget *dlgParent = (m_evalDepth == 0) ? (QWidget*)part->widget() : (QWidget*)this;
|
||
|
|
||
|
QString exceptionMsg = value.toString(exec).qstring();
|
||
|
|
||
|
// Syntax errors are a special case. For these we want to display the url & lineno,
|
||
|
// which isn't included in the exception messeage. So we work it out from the values
|
||
|
// passed to sourceParsed()
|
||
|
Object valueObj = Object::dynamicCast(value);
|
||
|
Object syntaxError = exec->interpreter()->builtinSyntaxError();
|
||
|
if (valueObj.isValid() && valueObj.get(exec,"constructor").imp() == syntaxError.imp()) {
|
||
|
Value sidValue = valueObj.get(exec,"sid");
|
||
|
if (sidValue.isA(NumberType)) { // sid is not set for Function() constructor
|
||
|
int sourceId = (int)sidValue.toNumber(exec);
|
||
|
assert(m_sourceFragments[sourceId]);
|
||
|
exceptionMsg = i18n("Parse error at %1 line %2")
|
||
|
.arg(m_sourceFragments[sourceId]->sourceFile->url)
|
||
|
.arg(m_sourceFragments[sourceId]->baseLine+m_sourceFragments[sourceId]->errorLine-1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool dontShowAgain = false;
|
||
|
if (m_execsCount == 0) {
|
||
|
// An exception occurred and we're not currently executing any code... this can
|
||
|
// happen in some cases e.g. a parse error, or native code accessing funcitons like
|
||
|
// Object::put()
|
||
|
QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1")
|
||
|
.arg(exceptionMsg);
|
||
|
KJSErrorDialog dlg(dlgParent,msg,false);
|
||
|
dlg.exec();
|
||
|
dontShowAgain = dlg.dontShowAgain();
|
||
|
}
|
||
|
else {
|
||
|
Context ctx = m_execs[m_execsCount-1]->context();
|
||
|
SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
|
||
|
QString msg = i18n("An error occurred while attempting to run a script on this page.\n\n%1 line %2:\n%3")
|
||
|
.arg(KStringHandler::rsqueeze( sourceFragment->sourceFile->url,80),
|
||
|
QString::number( sourceFragment->baseLine+ctx.curStmtFirstLine()-1),
|
||
|
exceptionMsg);
|
||
|
|
||
|
KJSErrorDialog dlg(dlgParent,msg,true);
|
||
|
dlg.exec();
|
||
|
dontShowAgain = dlg.dontShowAgain();
|
||
|
|
||
|
if (dlg.debugSelected()) {
|
||
|
m_mode = Next;
|
||
|
m_steppingDepth = m_execsCount-1;
|
||
|
enterSession(exec);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dontShowAgain) {
|
||
|
KConfig *config = kapp->config();
|
||
|
KConfigGroupSaver saver(config,QString::fromLatin1("Java/JavaScript Settings"));
|
||
|
config->writeEntry("ReportJavaScriptErrors",QVariant(false,0));
|
||
|
config->sync();
|
||
|
QByteArray data;
|
||
|
kapp->dcopClient()->send( "konqueror*", "KonquerorIface", "reparseConfiguration()", data );
|
||
|
}
|
||
|
|
||
|
return (m_mode != Stop);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::atStatement(KJS::ExecState *exec)
|
||
|
{
|
||
|
assert(m_execsCount > 0);
|
||
|
assert(m_execs[m_execsCount-1] == exec);
|
||
|
checkBreak(exec);
|
||
|
return (m_mode != Stop);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::enterContext(ExecState *exec)
|
||
|
{
|
||
|
if (m_execsCount >= m_execsAlloc) {
|
||
|
m_execsAlloc += 10;
|
||
|
m_execs = (ExecState**)realloc(m_execs,m_execsAlloc*sizeof(ExecState*));
|
||
|
}
|
||
|
m_execs[m_execsCount++] = exec;
|
||
|
|
||
|
if (m_mode == Step)
|
||
|
m_steppingDepth = m_execsCount-1;
|
||
|
|
||
|
checkBreak(exec);
|
||
|
return (m_mode != Stop);
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::exitContext(ExecState *exec, const Completion &/*completion*/)
|
||
|
{
|
||
|
assert(m_execsCount > 0);
|
||
|
assert(m_execs[m_execsCount-1] == exec);
|
||
|
|
||
|
checkBreak(exec);
|
||
|
|
||
|
m_execsCount--;
|
||
|
if (m_steppingDepth > m_execsCount-1)
|
||
|
m_steppingDepth = m_execsCount-1;
|
||
|
if (m_execsCount == 0)
|
||
|
updateContextList();
|
||
|
|
||
|
return (m_mode != Stop);
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::displaySourceFile(SourceFile *sourceFile, bool forceRefresh)
|
||
|
{
|
||
|
if (m_curSourceFile == sourceFile && !forceRefresh)
|
||
|
return;
|
||
|
sourceFile->ref();
|
||
|
m_sourceDisplay->setSource(sourceFile);
|
||
|
if (m_curSourceFile)
|
||
|
m_curSourceFile->deref();
|
||
|
m_curSourceFile = sourceFile;
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::setSourceLine(int sourceId, int lineno)
|
||
|
{
|
||
|
SourceFragment *source = m_sourceFragments[sourceId];
|
||
|
if (!source)
|
||
|
return;
|
||
|
|
||
|
SourceFile *sourceFile = source->sourceFile;
|
||
|
if (m_curSourceFile != source->sourceFile) {
|
||
|
for (int i = 0; i < m_sourceSel->count(); i++)
|
||
|
if (m_sourceSelFiles.at(i) == sourceFile)
|
||
|
m_sourceSel->setCurrentItem(i);
|
||
|
displaySourceFile(sourceFile,false);
|
||
|
}
|
||
|
m_sourceDisplay->setCurrentLine(source->baseLine+lineno-2);
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::setNextSourceInfo(QString url, int baseLine)
|
||
|
{
|
||
|
m_nextSourceUrl = url;
|
||
|
m_nextSourceBaseLine = baseLine;
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::sourceChanged(Interpreter *interpreter, QString url)
|
||
|
{
|
||
|
SourceFile *sourceFile = getSourceFile(interpreter,url);
|
||
|
if (sourceFile && m_curSourceFile == sourceFile)
|
||
|
displaySourceFile(sourceFile,true);
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::clearInterpreter(Interpreter *interpreter)
|
||
|
{
|
||
|
QMap<int,SourceFragment*>::Iterator it;
|
||
|
|
||
|
for (it = m_sourceFragments.begin(); it != m_sourceFragments.end(); ++it)
|
||
|
if (it.data() && it.data()->sourceFile->interpreter == interpreter)
|
||
|
it.data()->sourceFile->interpreter = 0;
|
||
|
}
|
||
|
|
||
|
SourceFile *KJSDebugWin::getSourceFile(Interpreter *interpreter, QString url)
|
||
|
{
|
||
|
QString key = QString("%1|%2").arg((long)interpreter).arg(url);
|
||
|
return m_sourceFiles[key];
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::setSourceFile(Interpreter *interpreter, QString url, SourceFile *sourceFile)
|
||
|
{
|
||
|
QString key = QString("%1|%2").arg((long)interpreter).arg(url);
|
||
|
sourceFile->ref();
|
||
|
if (SourceFile* oldFile = m_sourceFiles[key])
|
||
|
oldFile->deref();
|
||
|
m_sourceFiles[key] = sourceFile;
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::removeSourceFile(Interpreter *interpreter, QString url)
|
||
|
{
|
||
|
QString key = QString("%1|%2").arg((long)interpreter).arg(url);
|
||
|
if (SourceFile* oldFile = m_sourceFiles[key])
|
||
|
oldFile->deref();
|
||
|
m_sourceFiles.remove(key);
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::checkBreak(ExecState *exec)
|
||
|
{
|
||
|
if (m_breakpointCount > 0) {
|
||
|
Context ctx = m_execs[m_execsCount-1]->context();
|
||
|
if (haveBreakpoint(ctx.sourceId(),ctx.curStmtFirstLine(),ctx.curStmtLastLine())) {
|
||
|
m_mode = Next;
|
||
|
m_steppingDepth = m_execsCount-1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((m_mode == Step || m_mode == Next) && m_steppingDepth == m_execsCount-1)
|
||
|
enterSession(exec);
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::enterSession(ExecState *exec)
|
||
|
{
|
||
|
// This "enters" a new debugging session, i.e. enables usage of the debugging window
|
||
|
// It re-enters the qt event loop here, allowing execution of other parts of the
|
||
|
// program to continue while the script is stopped. We have to be a bit careful here,
|
||
|
// i.e. make sure the user can't quit the app, and disable other event handlers which
|
||
|
// could interfere with the debugging session.
|
||
|
if (!isVisible())
|
||
|
show();
|
||
|
|
||
|
m_mode = Continue;
|
||
|
|
||
|
if (m_execStates.isEmpty()) {
|
||
|
disableOtherWindows();
|
||
|
m_nextAction->setEnabled(true);
|
||
|
m_stepAction->setEnabled(true);
|
||
|
m_continueAction->setEnabled(true);
|
||
|
m_stopAction->setEnabled(true);
|
||
|
m_breakAction->setEnabled(false);
|
||
|
}
|
||
|
m_execStates.push(exec);
|
||
|
|
||
|
updateContextList();
|
||
|
|
||
|
qApp->enter_loop(); // won't return until leaveSession() is called
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::leaveSession()
|
||
|
{
|
||
|
// Disables debugging for this window and returns to execute the rest of the script
|
||
|
// (or aborts execution, if the user pressed stop). When this returns, the program
|
||
|
// will exit the qt event loop, i.e. return to whatever processing was being done
|
||
|
// before the debugger was stopped.
|
||
|
assert(!m_execStates.isEmpty());
|
||
|
|
||
|
m_execStates.pop();
|
||
|
|
||
|
if (m_execStates.isEmpty()) {
|
||
|
m_nextAction->setEnabled(false);
|
||
|
m_stepAction->setEnabled(false);
|
||
|
m_continueAction->setEnabled(false);
|
||
|
m_stopAction->setEnabled(false);
|
||
|
m_breakAction->setEnabled(true);
|
||
|
m_sourceDisplay->setCurrentLine(-1);
|
||
|
enableOtherWindows();
|
||
|
}
|
||
|
|
||
|
qApp->exit_loop();
|
||
|
}
|
||
|
|
||
|
void KJSDebugWin::updateContextList()
|
||
|
{
|
||
|
disconnect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
|
||
|
|
||
|
m_contextList->clear();
|
||
|
for (int i = 0; i < m_execsCount; i++)
|
||
|
m_contextList->insertItem(contextStr(m_execs[i]->context()));
|
||
|
|
||
|
if (m_execsCount > 0) {
|
||
|
m_contextList->setSelected(m_execsCount-1, true);
|
||
|
Context ctx = m_execs[m_execsCount-1]->context();
|
||
|
setSourceLine(ctx.sourceId(),ctx.curStmtFirstLine());
|
||
|
}
|
||
|
|
||
|
connect(m_contextList,SIGNAL(highlighted(int)),this,SLOT(slotShowFrame(int)));
|
||
|
}
|
||
|
|
||
|
QString KJSDebugWin::contextStr(const Context &ctx)
|
||
|
{
|
||
|
QString str = "";
|
||
|
SourceFragment *sourceFragment = m_sourceFragments[ctx.sourceId()];
|
||
|
QString url = sourceFragment->sourceFile->url;
|
||
|
int fileLineno = sourceFragment->baseLine+ctx.curStmtFirstLine()-1;
|
||
|
|
||
|
switch (ctx.codeType()) {
|
||
|
case GlobalCode:
|
||
|
str = QString("Global code at %1:%2").arg(url).arg(fileLineno);
|
||
|
break;
|
||
|
case EvalCode:
|
||
|
str = QString("Eval code at %1:%2").arg(url).arg(fileLineno);
|
||
|
break;
|
||
|
case FunctionCode:
|
||
|
if (!ctx.functionName().isNull())
|
||
|
str = QString("%1() at %2:%3").arg(ctx.functionName().qstring()).arg(url).arg(fileLineno);
|
||
|
else
|
||
|
str = QString("Anonymous function at %1:%2").arg(url).arg(fileLineno);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::setBreakpoint(int sourceId, int lineno)
|
||
|
{
|
||
|
if (haveBreakpoint(sourceId,lineno,lineno))
|
||
|
return false;
|
||
|
|
||
|
m_breakpointCount++;
|
||
|
m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
|
||
|
m_breakpointCount*sizeof(Breakpoint)));
|
||
|
m_breakpoints[m_breakpointCount-1].sourceId = sourceId;
|
||
|
m_breakpoints[m_breakpointCount-1].lineno = lineno;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::deleteBreakpoint(int sourceId, int lineno)
|
||
|
{
|
||
|
for (int i = 0; i < m_breakpointCount; i++) {
|
||
|
if (m_breakpoints[i].sourceId == sourceId && m_breakpoints[i].lineno == lineno) {
|
||
|
|
||
|
memmove(m_breakpoints+i,m_breakpoints+i+1,(m_breakpointCount-i-1)*sizeof(Breakpoint));
|
||
|
m_breakpointCount--;
|
||
|
m_breakpoints = static_cast<Breakpoint*>(realloc(m_breakpoints,
|
||
|
m_breakpointCount*sizeof(Breakpoint)));
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool KJSDebugWin::haveBreakpoint(SourceFile *sourceFile, int line0, int line1)
|
||
|
{
|
||
|
for (int i = 0; i < m_breakpointCount; i++) {
|
||
|
int sourceId = m_breakpoints[i].sourceId;
|
||
|
int lineno = m_breakpoints[i].lineno;
|
||
|
if (m_sourceFragments.contains(sourceId) &&
|
||
|
m_sourceFragments[sourceId]->sourceFile == sourceFile) {
|
||
|
int absLineno = m_sourceFragments[sourceId]->baseLine+lineno-1;
|
||
|
if (absLineno >= line0 && absLineno <= line1)
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
#include "kjs_debugwin.moc"
|
||
|
|
||
|
#endif // KJS_DEBUGGER
|