Commit 17ba81e9587f3202363b42f6662a2866506e9247

  • avatar
  • khansen
  • Thu Feb 04 15:54:28 CET 2010
Fix QtScript debugger hang/crash issue with updating locals model

Since the model is updated lazily, it's possible that a new model
has been set on the view (and the old model deleted) before the old
model has finished refreshing. This was discovered while holding
down the F11 key (Step Into) when debugging plasma.js in the
script/context2d example, which causes many events (and hence
asynchronous model updates) to trigger rapidly.
  
5454
5555#include <QtCore/qdebug.h>
5656#include <QtCore/qcoreapplication.h>
57#include <QtCore/qpointer.h>
5758#include <QtGui/qbrush.h>
5859#include <QtGui/qfont.h>
5960
371371 {
372372 if (!m_index.isValid()) {
373373 // nothing to do, the node has been removed
374 finish();
374375 return;
375376 }
376377 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
477477class InitModelJob : public QScriptDebuggerCommandSchedulerJob
478478{
479479public:
480 InitModelJob(QScriptDebuggerLocalsModelPrivate *model,
480 InitModelJob(QScriptDebuggerLocalsModel *model,
481481 int frameIndex,
482482 QScriptDebuggerCommandSchedulerInterface *scheduler)
483483 : QScriptDebuggerCommandSchedulerJob(scheduler),
486486
487487 void start()
488488 {
489 if (!m_model) {
490 // Model has been deleted.
491 finish();
492 return;
493 }
489494 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
490495 frontend.scheduleGetScopeChain(m_frameIndex);
491496 }
498498 void handleResponse(const QScriptDebuggerResponse &response,
499499 int)
500500 {
501 if (!m_model) {
502 // Model has been deleted.
503 finish();
504 return;
505 }
501506 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
507 QScriptDebuggerLocalsModelPrivate *model_d = QScriptDebuggerLocalsModelPrivate::get(m_model);
502508 switch (m_state) {
503509 case 0: {
504510 QScriptDebuggerValueList scopeChain = response.resultAsScriptValueList();
513513 QString name = QString::fromLatin1("Scope");
514514 if (i > 0)
515515 name.append(QString::fromLatin1(" (%0)").arg(i));
516 QModelIndex index = m_model->addTopLevelObject(name, scopeObject);
516 QModelIndex index = model_d->addTopLevelObject(name, scopeObject);
517517 if (i == 0)
518 m_model->emitScopeObjectAvailable(index);
518 model_d->emitScopeObjectAvailable(index);
519519 }
520520 frontend.scheduleGetThisObject(m_frameIndex);
521521 ++m_state;
522522 } break;
523523 case 1: {
524524 QScriptDebuggerValue thisObject = response.resultAsScriptValue();
525 m_model->addTopLevelObject(QLatin1String("this"), thisObject);
525 model_d->addTopLevelObject(QLatin1String("this"), thisObject);
526526 finish();
527527 } break;
528528 }
529529 }
530530
531531private:
532 QScriptDebuggerLocalsModelPrivate *m_model;
532 QPointer<QScriptDebuggerLocalsModel> m_model;
533533 int m_frameIndex;
534534 int m_state;
535535};
540540{
541541 Q_D(QScriptDebuggerLocalsModel);
542542 d->frameIndex = frameIndex;
543 QScriptDebuggerJob *job = new InitModelJob(d, frameIndex, d->commandScheduler);
543 QScriptDebuggerJob *job = new InitModelJob(this, frameIndex, d->commandScheduler);
544544 d->jobScheduler->scheduleJob(job);
545545}
546546
549549class SyncModelJob : public QScriptDebuggerCommandSchedulerJob
550550{
551551public:
552 SyncModelJob(QScriptDebuggerLocalsModelPrivate *model,
552 SyncModelJob(QScriptDebuggerLocalsModel *model,
553553 int frameIndex,
554554 QScriptDebuggerCommandSchedulerInterface *scheduler)
555555 : QScriptDebuggerCommandSchedulerJob(scheduler),
558558
559559 void start()
560560 {
561 if (!m_model) {
562 // Model has been deleted.
563 finish();
564 return;
565 }
561566 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
562567 frontend.scheduleGetScopeChain(m_frameIndex);
563568 }
570570 void handleResponse(const QScriptDebuggerResponse &response,
571571 int)
572572 {
573 if (!m_model) {
574 // Model has been deleted.
575 finish();
576 return;
577 }
573578 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
574579 switch (m_state) {
575580 case 0: {
584584 ++m_state;
585585 } break;
586586 case 1: {
587 QScriptDebuggerLocalsModelPrivate *model_d = QScriptDebuggerLocalsModelPrivate::get(m_model);
587588 QScriptDebuggerValue thisObject = response.resultAsScriptValue();
588589 m_topLevelObjects.append(thisObject);
589 bool equal = (m_topLevelObjects.size() == m_model->invisibleRootNode->children.size());
590 bool equal = (m_topLevelObjects.size() == model_d->invisibleRootNode->children.size());
590591 for (int i = 0; equal && (i < m_topLevelObjects.size()); ++i) {
591592 const QScriptDebuggerValue &object = m_topLevelObjects.at(i);
592 equal = (object == m_model->invisibleRootNode->children.at(i)->property.value());
593 equal = (object == model_d->invisibleRootNode->children.at(i)->property.value());
593594 }
594595 if (!equal) {
595596 // the scope chain and/or this-object changed, so invalidate the model.
596597 // we could try to be more clever, i.e. figure out
597598 // exactly which objects were popped/pushed
598 m_model->removeTopLevelNodes();
599 model_d->removeTopLevelNodes();
599600 for (int j = 0; j < m_topLevelObjects.size(); ++j) {
600601 const QScriptDebuggerValue &object = m_topLevelObjects.at(j);
601602 QString name;
607607 if (j > 0)
608608 name.append(QString::fromLatin1(" (%0)").arg(j));
609609 }
610 QModelIndex index = m_model->addTopLevelObject(name, object);
610 QModelIndex index = model_d->addTopLevelObject(name, object);
611611 if (j == 0)
612 m_model->emitScopeObjectAvailable(index);
612 model_d->emitScopeObjectAvailable(index);
613613 }
614614 } else {
615 m_model->syncTopLevelNodes();
615 model_d->syncTopLevelNodes();
616616 }
617617 finish();
618618 } break;
620620 }
621621
622622private:
623 QScriptDebuggerLocalsModelPrivate *m_model;
623 QPointer<QScriptDebuggerLocalsModel> m_model;
624624 int m_frameIndex;
625625 int m_state;
626626 QScriptDebuggerValueList m_topLevelObjects;
632632{
633633 Q_D(QScriptDebuggerLocalsModel);
634634 d->frameIndex = frameIndex;
635 QScriptDebuggerJob *job = new SyncModelJob(d, frameIndex, d->commandScheduler);
635 QScriptDebuggerJob *job = new SyncModelJob(this, frameIndex, d->commandScheduler);
636636 d->jobScheduler->scheduleJob(job);
637637}
638638
660660 {
661661 if (!m_index.isValid()) {
662662 // nothing to do, the node has been removed
663 finish();
663664 return;
664665 }
665666 QScriptDebuggerCommandSchedulerFrontend frontend(commandScheduler(), this);
  
7070
7171 bool hasChildren(const QModelIndex &parent) const
7272 {
73 if (!sourceModel())
74 return false;
7375 QModelIndex sourceParent = mapToSource(parent);
7476 if (parent.isValid() && !sourceParent.isValid())
7577 return false;
186186
187187void QScriptDebuggerLocalsWidgetPrivate::_q_expandIndex(const QModelIndex &index)
188188{
189 view->expand(proxy->mapFromSource(index));
189 if (view->model() == index.model())
190 view->expand(proxy->mapFromSource(index));
190191}
191192
192193class QScriptDebuggerLocalsItemDelegate