00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kjs_debugwin.h"
00022
00023 #ifdef KJS_DEBUGGER
00024
00025 #include <assert.h>
00026 #include <qlayout.h>
00027 #include <qpushbutton.h>
00028 #include <qtextedit.h>
00029 #include <qlistbox.h>
00030 #include <qlineedit.h>
00031 #include <qapplication.h>
00032 #include <qsplitter.h>
00033 #include <qcombobox.h>
00034 #include <qbitmap.h>
00035 #include <qwidgetlist.h>
00036
00037 #include <klocale.h>
00038 #include <kdebug.h>
00039 #include <kiconloader.h>
00040 #include <kmessagebox.h>
00041
00042 #include "kjs_dom.h"
00043 #include <kjs/ustring.h>
00044 #include <kjs/object.h>
00045 #include <kjs/function.h>
00046 #include <kjs/interpreter.h>
00047
00048 using namespace KJS;
00049
00050 KJSDebugWin * KJSDebugWin::kjs_html_debugger = 0;
00051
00052 bool FakeModal::eventFilter( QObject *o, QEvent *e )
00053 {
00054 switch (e->type()) {
00055 case QEvent::MouseButtonPress:
00056 case QEvent::MouseButtonRelease:
00057 case QEvent::MouseButtonDblClick:
00058 case QEvent::MouseMove:
00059 case QEvent::KeyPress:
00060 case QEvent::KeyRelease:
00061 case QEvent::Destroy:
00062 case QEvent::Close:
00063 case QEvent::Quit:
00064 while (o->parent())
00065 o = o->parent();
00066 if (o == modalWidget)
00067 return QWidget::eventFilter( o, e );
00068 else
00069 return TRUE;
00070 break;
00071 default:
00072 return QWidget::eventFilter( o, e );
00073 }
00074 }
00075
00076
00077 void FakeModal::enable(QWidget *modal)
00078 {
00079 QWidgetList *widgets = QApplication::allWidgets();
00080 QWidgetListIt it(*widgets);
00081 for (; it.current(); ++it)
00082 it.current()->installEventFilter(this);
00083 modalWidget = modal;
00084 }
00085
00086 void FakeModal::disable()
00087 {
00088 QWidgetList *widgets = QApplication::allWidgets();
00089 QWidgetListIt it(*widgets);
00090 for (; it.current(); ++it)
00091 it.current()->removeEventFilter(this);
00092 modalWidget = 0;
00093 }
00094
00095
00096
00097 QString StackFrame::toString()
00098 {
00099 QString str = "";
00100 if (!name.isNull())
00101 str.sprintf("%s() at sourceId %d, line %d",name.ascii(),sourceId,lineno);
00102 else
00103 str.sprintf("??? at sourceId %d, line %d",sourceId,lineno);
00104 return str;
00105 }
00106
00107 SourceFragment::SourceFragment(int sid, int bl, SourceFile *sf)
00108 {
00109 sourceId = sid;
00110 baseLine = bl;
00111 sourceFile = sf;
00112 sourceFile->ref();
00113 }
00114
00115 SourceFragment::~SourceFragment()
00116 {
00117 sourceFile->deref();
00118 }
00119
00120
00121
00122 KJSDebugWin::KJSDebugWin(QWidget *parent, const char *name)
00123 : QWidget(parent, name),
00124 m_inSession(false),
00125 m_curSourceFile(0)
00126 {
00127 setCaption(i18n("JavaScript Debugger"));
00128 QVBoxLayout *vl = new QVBoxLayout(this, 5);
00129
00130
00131 QSplitter *splitter = new QSplitter(this);
00132 QFont font("courier",10);
00133
00134 m_frameList = new QListBox(splitter);
00135 m_frameList->setFont(font);
00136 m_frameList->setMinimumSize(100,200);
00137 connect(m_frameList,SIGNAL(highlighted(int)),this,SLOT(showFrame(int)));
00138
00139
00140 QWidget *sourceSelDisplay = new QWidget(splitter);
00141 QVBoxLayout *ssdvl = new QVBoxLayout(sourceSelDisplay);
00142
00143
00144 m_sourceSel = new QComboBox(sourceSelDisplay);
00145 connect(m_sourceSel,SIGNAL(activated(int)),this,SLOT(sourceSelected(int)));
00146 ssdvl->addWidget(m_sourceSel);
00147
00148 m_sourceDisplay = new QListBox(sourceSelDisplay);
00149 m_sourceDisplay->setFont(font);
00150 ssdvl->addWidget(m_sourceDisplay);
00151
00152 vl->addWidget(splitter);
00153
00154 QValueList<int> splitSizes;
00155 splitSizes.insert(splitSizes.end(),200);
00156 splitSizes.insert(splitSizes.end(),400);
00157 splitter->setSizes(splitSizes);
00158
00159
00160
00161 QHBoxLayout *hl1 = new QHBoxLayout(vl);
00162 m_evalEdit = new QLineEdit(this);
00163 m_evalButton = new QPushButton(i18n("&Evaluate"),this);
00164 m_evalButton->setEnabled(false);
00165 hl1->addWidget(m_evalEdit);
00166 hl1->addWidget(m_evalButton);
00167 connect(m_evalButton, SIGNAL(clicked()), SLOT(eval()));
00168 connect(m_evalEdit, SIGNAL(returnPressed()), SLOT(eval()));
00169
00170
00171 QHBoxLayout *hl2 = new QHBoxLayout(vl);
00172 m_nextButton = new QPushButton(i18n("&Next"), this);
00173 m_stepButton = new QPushButton(i18n("&Step"), this);
00174 m_continueButton = new QPushButton(i18n("&Continue"), this);
00175 m_stopButton = new QPushButton(i18n("St&op"), this);
00176 m_breakButton = new QPushButton(i18n("&Break at next Statement"), this);
00177 m_breakpointButton = new QPushButton(i18n("&Toggle Breakpoint"), this);
00178 hl2->addWidget(m_nextButton);
00179 hl2->addWidget(m_stepButton);
00180 hl2->addWidget(m_continueButton);
00181 hl2->addWidget(m_stopButton);
00182 hl2->addWidget(m_breakButton);
00183 hl2->addWidget(m_breakpointButton);
00184 hl2->addStretch();
00185
00186 connect(m_nextButton, SIGNAL(clicked()), SLOT(next()));
00187 connect(m_stepButton, SIGNAL(clicked()), SLOT(step()));
00188 connect(m_continueButton, SIGNAL(clicked()), SLOT(cont()));
00189 connect(m_stopButton, SIGNAL(clicked()), SLOT(stop()));
00190 connect(m_breakButton, SIGNAL(clicked()), SLOT(breakNext()));
00191 connect(m_breakpointButton, SIGNAL(clicked()), SLOT(toggleBreakpoint()));
00192
00193 m_nextButton->setEnabled(false);
00194 m_stepButton->setEnabled(false);
00195 m_continueButton->setEnabled(false);
00196 m_stopButton->setEnabled(false);
00197 m_breakButton->setEnabled(true);
00198 m_breakpointButton->setEnabled(false);
00199
00200
00201 m_frames.setAutoDelete(true);
00202 StackFrame *sf = new StackFrame(-1,-1,"Global code",false);
00203 sf->next = true;
00204 m_frames.append(sf);
00205
00206
00207
00208 setMinimumSize(300,200);
00209 resize(600,450);
00210 m_mode = Continue;
00211 m_sourceBreakpoints = 0;
00212
00213 KIconLoader loader;
00214 m_stopIcon = loader.loadIcon("stop",KIcon::Small);
00215
00216 m_emptyIcon = QPixmap(m_stopIcon.width(),m_stopIcon.height());
00217 QBitmap emptyMask(m_stopIcon.width(),m_stopIcon.height(),true);
00218
00219 m_emptyIcon.setMask(emptyMask);
00220
00221 m_nextSourceBaseLine = 0;
00222 m_nextSourceUrl = "";
00223
00224 updateFrameList();
00225 m_inSession = false;
00226 m_curExecState = 0;
00227 }
00228
00229 KJSDebugWin::~KJSDebugWin()
00230 {
00231 }
00232
00233
00234 KJSDebugWin *KJSDebugWin::createInstance()
00235 {
00236 assert(!kjs_html_debugger);
00237 kjs_html_debugger = new KJSDebugWin();
00238 kjs_html_debugger->show();
00239 return kjs_html_debugger;
00240 }
00241
00242 void KJSDebugWin::destroyInstance()
00243 {
00244 assert(kjs_html_debugger);
00245 kjs_html_debugger->hide();
00246 delete kjs_html_debugger;
00247 }
00248
00249 void KJSDebugWin::next()
00250 {
00251 m_mode = Next;
00252 leaveSession();
00253 }
00254
00255 void KJSDebugWin::step()
00256 {
00257 m_mode = Step;
00258 leaveSession();
00259 }
00260
00261 void KJSDebugWin::cont()
00262 {
00263 m_mode = Continue;
00264 leaveSession();
00265 }
00266
00267 void KJSDebugWin::stop()
00268 {
00269 m_mode = Stop;
00270 leaveSession();
00271 }
00272
00273 void KJSDebugWin::breakNext()
00274 {
00275 m_mode = Step;
00276 }
00277
00278 void KJSDebugWin::toggleBreakpoint()
00279 {
00280 int line = m_sourceDisplay->currentItem();
00281 if (line >= 0) {
00282 QString text(m_sourceDisplay->item(line)->text());
00283 m_sourceDisplay->removeItem(line);
00284 QListBoxPixmap *item;
00285 if (setBreakpoint(m_frames.last()->sourceId, line)) {
00286 item = new QListBoxPixmap(m_stopIcon,text);
00287 } else {
00288 deleteBreakpoint(m_frames.last()->sourceId, line);
00289 item = new QListBoxPixmap(m_emptyIcon,text);
00290 }
00291 m_sourceDisplay->insertItem(item, line);
00292 m_sourceDisplay->setCurrentItem(line);
00293 }
00294 }
00295
00296 void KJSDebugWin::showFrame(int frameno)
00297 {
00298 StackFrame *frame = m_frames.at(frameno);
00299 if (!frame)
00300 return;
00301 highLight(frame->sourceId,frame->lineno);
00302 }
00303
00304 void KJSDebugWin::sourceSelected(int sourceSelIndex)
00305 {
00306
00307
00308 if (sourceSelIndex < 0 || sourceSelIndex >= (int)m_sourceSel->count())
00309 return;
00310 SourceFile *sourceFile = m_sourceSelFiles[sourceSelIndex];
00311 bool newsource = m_curSourceFile != sourceFile;
00312 m_curSourceFile = sourceFile;
00313
00314 SourceFragment *lastFragment = 0;
00315 StackFrame *curFrame = m_frames.at(m_frameList->currentItem() >= 0 ? m_frameList->currentItem() : m_frames.count()-1);
00316 if (curFrame)
00317 lastFragment = m_sourceFragments[curFrame->sourceId];
00318 if (newsource)
00319 setCode(sourceFile->code, curFrame ? curFrame->sourceId : -1);
00320 if (lastFragment && lastFragment->sourceFile == m_curSourceFile) {
00321 m_sourceDisplay->setCurrentItem(lastFragment->baseLine+curFrame->lineno);
00322 } else
00323 m_sourceDisplay->setCurrentItem(-1);
00324 }
00325
00326 void KJSDebugWin::eval()
00327 {
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347 }
00348
00349 void KJSDebugWin::closeEvent(QCloseEvent *e)
00350 {
00351 if (m_inSession)
00352 leaveSession();
00353 return QWidget::closeEvent(e);
00354 }
00355
00356 bool KJSDebugWin::sourceParsed(KJS::ExecState * exec, int sourceId,
00357 const KJS::UString &source, int )
00358 {
00359
00360
00361
00362 SourceFile *sourceFile = m_sourceFiles[m_nextSourceUrl];
00363 if (!sourceFile) {
00364 sourceFile = new SourceFile("(unknown)",source.qstring(),m_sourceSel->count());
00365 m_sourceSelFiles[sourceFile->index] = sourceFile;
00366 if (m_nextSourceUrl.isNull() || m_nextSourceUrl == "")
00367 m_sourceSel->insertItem("???");
00368 else
00369 m_sourceSel->insertItem(m_nextSourceUrl);
00370 }
00371
00372 SourceFragment *sf = new SourceFragment(sourceId,m_nextSourceBaseLine,sourceFile);
00373 m_sourceFragments[sourceId] = sf;
00374
00375
00376 m_nextSourceBaseLine = 0;
00377 m_nextSourceUrl = "";
00378
00379 return (m_mode != Stop);
00380 }
00381
00382 bool KJSDebugWin::sourceUnused(KJS::ExecState * , int sourceId)
00383 {
00384
00385
00386 SourceFragment *fragment = m_sourceFragments[sourceId];
00387 if (fragment) {
00388 m_sourceFragments.erase(sourceId);
00389 SourceFile *sourceFile = fragment->sourceFile;
00390 if (sourceFile->hasOneRef()) {
00391 m_sourceSel->removeItem(sourceFile->index);
00392 for (int i = sourceFile->index; i < m_sourceSel->count(); i++) {
00393 m_sourceSelFiles[i+1]->index--;
00394 m_sourceSelFiles[i] = m_sourceSelFiles[i+1];
00395 }
00396 m_sourceSelFiles.erase(m_sourceSel->count());
00397 m_sourceSel->removeItem(m_sourceSel->count()-1);
00398 }
00399 delete fragment;
00400 }
00401 return (m_mode != Stop);
00402 }
00403
00404 bool KJSDebugWin::exception(KJS::ExecState *exec, int ,
00405 int , KJS::Object &exceptionObj)
00406 {
00407
00408 KMessageBox::error(this, exceptionObj.toString(exec).qstring(), "JavaScript error");
00409 return (m_mode != Stop);
00410 }
00411
00412 bool KJSDebugWin::atStatement(KJS::ExecState *exec, int sourceId,
00413 int firstLine, int lastLine)
00414 {
00415 KJS::ExecState *oldCurExecState = m_curExecState;
00416 m_curExecState = exec;
00417
00418 if (haveBreakpoint(sourceId,firstLine, lastLine)) {
00419 m_mode = Next;
00420 m_frames.last()->next = true;
00421 }
00422
00423 m_frames.last()->sourceId = sourceId;
00424 m_frames.last()->lineno = firstLine;
00425
00426 if (m_mode == KJSDebugWin::Step || m_mode == KJSDebugWin::Next) {
00427 if (m_frames.last()->next)
00428 enterSession();
00429 }
00430
00431 m_curExecState = oldCurExecState;
00432 return (m_mode != Stop);
00433 }
00434
00435 bool KJSDebugWin::callEvent(KJS::ExecState *exec, int sourceId, int lineno,
00436 KJS::Object &function, const KJS::List & )
00437 {
00438
00439 KJS::ExecState *oldCurExecState = m_curExecState;
00440 m_curExecState = exec;
00441 KJS::FunctionImp *fimp = static_cast<KJS::FunctionImp*>(function.imp());
00442 QString name = fimp->name().qstring();
00443 StackFrame *sf = new StackFrame(sourceId,lineno,name,m_mode == Step);
00444 m_frames.append(sf);
00445 if (m_mode == Step)
00446 enterSession();
00447 m_curExecState = oldCurExecState;
00448 return (m_mode != Stop);
00449 }
00450
00451 bool KJSDebugWin::returnEvent(KJS::ExecState *exec, int sourceId, int lineno,
00452 KJS::Object & )
00453 {
00454
00455 KJS::ExecState *oldCurExecState = m_curExecState;
00456 m_curExecState = exec;
00457 m_frames.last()->sourceId = sourceId;
00458 m_frames.last()->lineno = lineno;
00459 if (m_frames.last()->step)
00460 enterSession();
00461 m_frames.removeLast();
00462
00463 m_curExecState = oldCurExecState;
00464 return (m_mode != Stop);
00465 }
00466
00467 void KJSDebugWin::setCode(const QString &code, int sourceId)
00468 {
00469 const QChar *chars = code.unicode();
00470 uint len = code.length();
00471 QChar newLine('\n');
00472 QChar cr('\r');
00473 QChar tab('\t');
00474 QString tabstr(" ");
00475 QString line;
00476 m_sourceDisplay->clear();
00477 int numlines = 0;
00478 for (uint i = 0; i < len; i++) {
00479 if (chars[i] == cr)
00480 continue;
00481 else if (chars[i] == newLine) {
00482 m_sourceDisplay->insertItem(new QListBoxPixmap(haveBreakpoint(sourceId, numlines, numlines) ? m_stopIcon : m_emptyIcon,line));
00483 numlines++;
00484 line = "";
00485 } else if (chars[i] == tab) {
00486 line += tabstr;
00487 } else
00488 line += chars[i];
00489 }
00490 if (line.length())
00491 m_sourceDisplay->insertItem(new QListBoxPixmap(haveBreakpoint(sourceId, numlines, numlines) ? m_stopIcon : m_emptyIcon,line));
00492 }
00493
00494 void KJSDebugWin::highLight(int sourceId, int line)
00495 {
00496 if (!isVisible())
00497 show();
00498
00499 SourceFragment *source = m_sourceFragments[sourceId];
00500 if (!source)
00501 return;
00502
00503 SourceFile *sourceFile = source->sourceFile;
00504 if (m_curSourceFile != source->sourceFile) {
00505 m_sourceSel->setCurrentItem(source->sourceFile->index);
00506 setCode(sourceFile->code, sourceId);
00507 }
00508 m_curSourceFile = source->sourceFile;
00509 if (line > 0)
00510 m_sourceDisplay->setCurrentItem(line+source->baseLine-1);
00511 else
00512 m_sourceDisplay->setCurrentItem(-1);
00513 }
00514
00515 void KJSDebugWin::setNextSourceInfo(QString url, int baseLine)
00516 {
00517 m_nextSourceUrl = url;
00518 m_nextSourceBaseLine = baseLine;
00519 }
00520
00521 void KJSDebugWin::setSourceFile(QString url, QString code)
00522 {
00523 SourceFile *existing = m_sourceFiles[url];
00524 int newindex = m_sourceSel->count();
00525 if (existing) {
00526 newindex = existing->index;
00527 m_sourceSel->removeItem(existing->index);
00528 existing->deref();
00529 }
00530 SourceFile *newSF = new SourceFile(url,code,newindex);
00531 m_sourceFiles[url] = newSF;
00532 m_sourceSelFiles[newindex] = newSF;
00533 m_sourceSel->insertItem(url,newindex);
00534 }
00535
00536 void KJSDebugWin::appendSourceFile(QString url, QString code)
00537 {
00538 SourceFile *existing = m_sourceFiles[url];
00539 if (!existing) {
00540 setSourceFile(url,code);
00541 return;
00542 }
00543 existing->code.append(code);
00544 }
00545
00546 void KJSDebugWin::enterSession()
00547 {
00548
00549
00550
00551
00552 assert(!m_inSession);
00553 m_fakeModal.enable(this);
00554 m_inSession = true;
00555 m_mode = Continue;
00556
00557 m_nextButton->setEnabled(true);
00558 m_stepButton->setEnabled(true);
00559 m_continueButton->setEnabled(true);
00560 m_stopButton->setEnabled(true);
00561 m_evalButton->setEnabled(true);
00562 m_breakButton->setEnabled(false);
00563 m_breakpointButton->setEnabled(true);
00564 updateFrameList();
00565
00566 qApp->enter_loop();
00567 assert(!m_inSession);
00568 }
00569
00570 void KJSDebugWin::leaveSession()
00571 {
00572
00573
00574
00575
00576 assert(m_inSession);
00577 m_nextButton->setEnabled(false);
00578 m_stepButton->setEnabled(false);
00579 m_continueButton->setEnabled(false);
00580 m_stopButton->setEnabled(false);
00581 m_evalButton->setEnabled(false);
00582 m_breakButton->setEnabled(true);
00583 m_breakpointButton->setEnabled(false);
00584 m_inSession = false;
00585 qApp->exit_loop();
00586 m_fakeModal.disable();
00587 }
00588
00589 void KJSDebugWin::updateFrameList()
00590 {
00591 uint frameno;
00592 disconnect(m_frameList,SIGNAL(highlighted(int)),this,SLOT(showFrame(int)));
00593 m_frameList->clear();
00594 for (frameno = 0; frameno < m_frames.count(); frameno++) {
00595 m_frameList->insertItem(m_frames.at(frameno)->toString(),frameno);
00596 }
00597 m_frameList->setSelected(m_frameList->count()-1, true);
00598 highLight(m_frames.last()->sourceId,m_frames.last()->lineno);
00599 connect(m_frameList,SIGNAL(highlighted(int)),this,SLOT(showFrame(int)));
00600 }
00601
00602 bool KJSDebugWin::setBreakpoint(int sourceId, int line)
00603 {
00604 if (haveBreakpoint(sourceId,line,line))
00605 return false;
00606
00607 SourceBreakpoints *sbp = m_sourceBreakpoints;
00608 while(sbp && sbp->sourceId != sourceId)
00609 sbp = sbp->next;
00610 if (!sbp) {
00611 sbp = new SourceBreakpoints;
00612 sbp->sourceId = sourceId;
00613 sbp->breakpoints = 0;
00614 sbp->next = m_sourceBreakpoints;
00615 m_sourceBreakpoints = sbp;
00616 }
00617
00618 Breakpoint *newbp = new Breakpoint;
00619 newbp->lineno = line;
00620 newbp->next = sbp->breakpoints;
00621 sbp->breakpoints = newbp;
00622
00623 return true;
00624 }
00625
00626 bool KJSDebugWin::deleteBreakpoint(int sourceId, int line)
00627 {
00628 for (SourceBreakpoints *sbp = m_sourceBreakpoints; sbp; sbp = sbp->next) {
00629 if (sbp->sourceId == sourceId) {
00630
00631 Breakpoint *bp = sbp->breakpoints;
00632 if (bp && bp->lineno == line) {
00633
00634 Breakpoint *next = bp->next;
00635 delete bp;
00636 sbp->breakpoints = next;
00637 return true;
00638 }
00639
00640 while (bp->next && bp->next->lineno != line)
00641 bp = bp->next;
00642 if (bp->next && bp->next->lineno == line) {
00643
00644 Breakpoint *next = bp->next->next;
00645 delete bp->next;
00646 bp->next = next;
00647 return true;
00648 }
00649 return false;
00650 }
00651 }
00652
00653 return false;
00654 }
00655
00656 void KJSDebugWin::clearAllBreakpoints(int sourceId)
00657 {
00658 SourceBreakpoints *nextsbp = 0;
00659 for (SourceBreakpoints *sbp = m_sourceBreakpoints; sbp; sbp = nextsbp) {
00660 nextsbp = sbp->next;
00661 if (sourceId == -1 || sbp->sourceId == sourceId) {
00662 Breakpoint *nextbp;
00663 for (Breakpoint *bp = sbp->breakpoints; bp; bp = bp->next) {
00664 nextbp = bp->next;
00665 delete bp;
00666 }
00667 delete sbp;
00668 }
00669 }
00670 }
00671
00672 int KJSDebugWin::breakpointLine(int sourceId, int line0, int line1)
00673 {
00674 for (SourceBreakpoints *sbp = m_sourceBreakpoints; sbp; sbp = sbp->next) {
00675 if (sbp->sourceId == sourceId) {
00676
00677 for (Breakpoint *bp = sbp->breakpoints; bp; bp = bp->next) {
00678 if (bp->lineno >= 0 && bp->lineno >= line0 && bp->lineno <= line1)
00679 return bp->lineno;
00680 }
00681 return -1;
00682 }
00683 }
00684
00685 return -1;
00686 }
00687
00688 bool KJSDebugWin::haveBreakpoint(int sourceId, int line0, int line1)
00689 {
00690 return (breakpointLine(sourceId,line0,line1) != -1);
00691 }
00692
00693 #include "kjs_debugwin.moc"
00694
00695 #endif // KJS_DEBUGGER