src/scripttools/debugging/qscriptdebuggercommandexecutor.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/scripttools/debugging/qscriptdebuggercommandexecutor.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,522 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSCriptTools module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qscriptdebuggercommandexecutor_p.h"
+
+#include "qscriptdebuggerbackend_p.h"
+#include "qscriptdebuggercommand_p.h"
+#include "qscriptdebuggerresponse_p.h"
+#include "qscriptdebuggervalue_p.h"
+#include "qscriptdebuggervalueproperty_p.h"
+#include "qscriptbreakpointdata_p.h"
+#include "qscriptobjectsnapshot_p.h"
+#include "qscriptdebuggerobjectsnapshotdelta_p.h"
+
+#include <QtCore/qstringlist.h>
+#include <QtScript/qscriptengine.h>
+#include <QtScript/qscriptcontextinfo.h>
+#include <QtScript/qscriptvalueiterator.h>
+#include <QtCore/qdebug.h>
+
+Q_DECLARE_METATYPE(QScriptScriptsDelta)
+Q_DECLARE_METATYPE(QScriptDebuggerValueProperty)
+Q_DECLARE_METATYPE(QScriptDebuggerValuePropertyList)
+Q_DECLARE_METATYPE(QScriptDebuggerObjectSnapshotDelta)
+
+QT_BEGIN_NAMESPACE
+
+/*!
+  \since 4.5
+  \class QScriptDebuggerCommandExecutor
+  \internal
+
+  \brief The QScriptDebuggerCommandExecutor applies debugger commands to a back-end.
+
+  The execute() function takes a command (typically produced by a
+  QScriptDebuggerFrontend) and applies it to a QScriptDebuggerBackend.
+
+  \sa QScriptDebuggerCommmand
+*/
+
+class QScriptDebuggerCommandExecutorPrivate
+{
+public:
+    QScriptDebuggerCommandExecutorPrivate();
+    ~QScriptDebuggerCommandExecutorPrivate();
+};
+
+QScriptDebuggerCommandExecutorPrivate::QScriptDebuggerCommandExecutorPrivate()
+{
+}
+
+QScriptDebuggerCommandExecutorPrivate::~QScriptDebuggerCommandExecutorPrivate()
+{
+}
+
+QScriptDebuggerCommandExecutor::QScriptDebuggerCommandExecutor()
+    : d_ptr(new QScriptDebuggerCommandExecutorPrivate())
+{
+}
+
+QScriptDebuggerCommandExecutor::~QScriptDebuggerCommandExecutor()
+{
+}
+
+static bool isPrefixOf(const QString &prefix, const QString &what)
+{
+    return ((what.length() > prefix.length())
+            && what.startsWith(prefix));
+}
+
+/*!
+  Applies the given \a command to the given \a backend.
+*/
+QScriptDebuggerResponse QScriptDebuggerCommandExecutor::execute(
+    QScriptDebuggerBackend *backend,
+    const QScriptDebuggerCommand &command)
+{
+    QScriptDebuggerResponse response;
+    switch (command.type()) {
+    case QScriptDebuggerCommand::None:
+        break;
+
+    case QScriptDebuggerCommand::Interrupt:
+        backend->interruptEvaluation();
+        break;
+
+    case QScriptDebuggerCommand::Continue:
+        if (backend->engine()->isEvaluating()) {
+            backend->continueEvalution();
+            response.setAsync(true);
+        }
+        break;
+
+    case QScriptDebuggerCommand::StepInto: {
+        QVariant attr = command.attribute(QScriptDebuggerCommand::StepCount);
+        int count = attr.isValid() ? attr.toInt() : 1;
+        backend->stepInto(count);
+        response.setAsync(true);
+    }   break;
+
+    case QScriptDebuggerCommand::StepOver: {
+        QVariant attr = command.attribute(QScriptDebuggerCommand::StepCount);
+        int count = attr.isValid() ? attr.toInt() : 1;
+        backend->stepOver(count);
+        response.setAsync(true);
+    }   break;
+
+    case QScriptDebuggerCommand::StepOut:
+        backend->stepOut();
+        response.setAsync(true);
+        break;
+
+    case QScriptDebuggerCommand::RunToLocation:
+        backend->runToLocation(command.fileName(), command.lineNumber());
+        response.setAsync(true);
+        break;
+
+    case QScriptDebuggerCommand::RunToLocationByID:
+        backend->runToLocation(command.scriptId(), command.lineNumber());
+        response.setAsync(true);
+        break;
+
+    case QScriptDebuggerCommand::ForceReturn: {
+        int contextIndex = command.contextIndex();
+        QScriptDebuggerValue value = command.scriptValue();
+        QScriptEngine *engine = backend->engine();
+        QScriptValue realValue = value.toScriptValue(engine);
+        backend->returnToCaller(contextIndex, realValue);
+        response.setAsync(true);
+    }   break;
+
+    case QScriptDebuggerCommand::Resume:
+        backend->resume();
+        response.setAsync(true);
+        break;
+
+    case QScriptDebuggerCommand::SetBreakpoint: {
+        QScriptBreakpointData data = command.breakpointData();
+        if (!data.isValid())
+            data = QScriptBreakpointData(command.fileName(), command.lineNumber());
+        int id = backend->setBreakpoint(data);
+        response.setResult(id);
+    }   break;
+
+    case QScriptDebuggerCommand::DeleteBreakpoint: {
+        int id = command.breakpointId();
+        if (!backend->deleteBreakpoint(id))
+            response.setError(QScriptDebuggerResponse::InvalidBreakpointID);
+    }   break;
+
+    case QScriptDebuggerCommand::DeleteAllBreakpoints:
+        backend->deleteAllBreakpoints();
+        break;
+
+    case QScriptDebuggerCommand::GetBreakpoints: {
+        QScriptBreakpointMap bps = backend->breakpoints();
+        if (!bps.isEmpty())
+            response.setResult(bps);
+    }   break;
+
+    case QScriptDebuggerCommand::GetBreakpointData: {
+        int id = command.breakpointId();
+        QScriptBreakpointData data = backend->breakpointData(id);
+        if (data.isValid())
+            response.setResult(data);
+        else
+            response.setError(QScriptDebuggerResponse::InvalidBreakpointID);
+    }   break;
+
+    case QScriptDebuggerCommand::SetBreakpointData: {
+        int id = command.breakpointId();
+        QScriptBreakpointData data = command.breakpointData();
+        if (!backend->setBreakpointData(id, data))
+            response.setError(QScriptDebuggerResponse::InvalidBreakpointID);
+    }   break;
+
+    case QScriptDebuggerCommand::GetScripts: {
+        QScriptScriptMap scripts = backend->scripts();
+        if (!scripts.isEmpty())
+            response.setResult(scripts);
+    }   break;
+
+    case QScriptDebuggerCommand::GetScriptData: {
+        qint64 id = command.scriptId();
+        QScriptScriptData data = backend->scriptData(id);
+        if (data.isValid())
+            response.setResult(data);
+        else
+            response.setError(QScriptDebuggerResponse::InvalidScriptID);
+    }   break;
+
+    case QScriptDebuggerCommand::ScriptsCheckpoint:
+        backend->scriptsCheckpoint();
+        response.setResult(qVariantFromValue(backend->scriptsDelta()));
+        break;
+
+    case QScriptDebuggerCommand::GetScriptsDelta:
+        response.setResult(qVariantFromValue(backend->scriptsDelta()));
+        break;
+
+    case QScriptDebuggerCommand::ResolveScript:
+        response.setResult(backend->resolveScript(command.fileName()));
+        break;
+
+    case QScriptDebuggerCommand::GetBacktrace:
+        response.setResult(backend->backtrace());
+        break;
+
+    case QScriptDebuggerCommand::GetContextCount:
+        response.setResult(backend->contextCount());
+        break;
+
+    case QScriptDebuggerCommand::GetContextState: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        if (ctx)
+            response.setResult(static_cast<int>(ctx->state()));
+        else
+            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
+    }   break;
+
+    case QScriptDebuggerCommand::GetContextID: {
+        int idx = command.contextIndex();
+        if ((idx >= 0) && (idx < backend->contextCount()))
+            response.setResult(backend->contextIds()[idx]);
+        else
+            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
+    }   break;
+
+    case QScriptDebuggerCommand::GetContextInfo: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        if (ctx)
+            response.setResult(QScriptContextInfo(ctx));
+        else
+            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
+    }   break;
+
+    case QScriptDebuggerCommand::GetThisObject: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        if (ctx)
+            response.setResult(ctx->thisObject());
+        else
+            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
+    }   break;
+
+    case QScriptDebuggerCommand::GetActivationObject: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        if (ctx)
+            response.setResult(ctx->activationObject());
+        else
+            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
+    }   break;
+
+    case QScriptDebuggerCommand::GetScopeChain: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        if (ctx) {
+            QScriptDebuggerValueList dest;
+            QScriptValueList src = ctx->scopeChain();
+            for (int i = 0; i < src.size(); ++i)
+                dest.append(src.at(i));
+            response.setResult(dest);
+        } else {
+            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
+        }
+    }   break;
+
+    case QScriptDebuggerCommand::ContextsCheckpoint: {
+        response.setResult(qVariantFromValue(backend->contextsCheckpoint()));
+    }   break;
+
+    case QScriptDebuggerCommand::GetPropertyExpressionValue: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        int lineNumber = command.lineNumber();
+        QVariant attr = command.attribute(QScriptDebuggerCommand::UserAttribute);
+        QStringList path = attr.toStringList();
+        if (!ctx || path.isEmpty())
+            break;
+        QScriptContextInfo ctxInfo(ctx);
+        if (ctx->callee().isValid()
+            && ((lineNumber < ctxInfo.functionStartLineNumber())
+                || (lineNumber > ctxInfo.functionEndLineNumber()))) {
+            break;
+        }
+        QScriptValueList objects;
+        int pathIndex = 0;
+        if (path.at(0) == QLatin1String("this")) {
+            objects.append(ctx->thisObject());
+            ++pathIndex;
+        } else {
+            objects << ctx->scopeChain();
+        }
+        for (int i = 0; i < objects.size(); ++i) {
+            QScriptValue val = objects.at(i);
+            for (int j = pathIndex; val.isValid() && (j < path.size()); ++j) {
+                val = val.property(path.at(j));
+            }
+            if (val.isValid()) {
+                bool hadException = (ctx->state() == QScriptContext::ExceptionState);
+                QString str = val.toString();
+                if (!hadException && backend->engine()->hasUncaughtException())
+                    backend->engine()->clearExceptions();
+                response.setResult(str);
+                break;
+            }
+        }
+    }   break;
+
+    case QScriptDebuggerCommand::GetCompletions: {
+        QScriptContext *ctx = backend->context(command.contextIndex());
+        QVariant attr = command.attribute(QScriptDebuggerCommand::UserAttribute);
+        QStringList path = attr.toStringList();
+        if (!ctx || path.isEmpty())
+            break;
+        QScriptValueList objects;
+        QString prefix = path.last();
+        QSet<QString> matches;
+        if (path.size() > 1) {
+            const QString &topLevelIdent = path.at(0);
+            QScriptValue obj;
+            if (topLevelIdent == QLatin1String("this")) {
+                obj = ctx->thisObject();
+            } else {
+                QScriptValueList scopeChain;
+                scopeChain = ctx->scopeChain();
+                for (int i = 0; i < scopeChain.size(); ++i) {
+                    QScriptValue oo = scopeChain.at(i).property(topLevelIdent);
+                    if (oo.isObject()) {
+                        obj = oo;
+                        break;
+                    }
+                }
+            }
+            for (int i = 1; obj.isObject() && (i < path.size()-1); ++i)
+                obj = obj.property(path.at(i));
+            if (obj.isValid())
+                objects.append(obj);
+        } else {
+            objects << ctx->scopeChain();
+            QStringList keywords;
+            keywords.append(QString::fromLatin1("this"));
+            keywords.append(QString::fromLatin1("true"));
+            keywords.append(QString::fromLatin1("false"));
+            keywords.append(QString::fromLatin1("null"));
+            for (int i = 0; i < keywords.size(); ++i) {
+                const QString &kwd = keywords.at(i);
+                if (isPrefixOf(prefix, kwd))
+                    matches.insert(kwd);
+            }
+        }
+
+        for (int i = 0; i < objects.size(); ++i) {
+            QScriptValue obj = objects.at(i);
+            while (obj.isObject()) {
+                QScriptValueIterator it(obj);
+                while (it.hasNext()) {
+                    it.next();
+                    QString propertyName = it.name();
+                    if (isPrefixOf(prefix, propertyName))
+                        matches.insert(propertyName);
+                }
+                obj = obj.prototype();
+            }
+        }
+        QStringList matchesList = matches.toList();
+        qStableSort(matchesList);
+        response.setResult(matchesList);
+    }   break;
+
+    case QScriptDebuggerCommand::NewScriptObjectSnapshot: {
+        int id = backend->newScriptObjectSnapshot();
+        response.setResult(id);
+    }   break;
+
+    case QScriptDebuggerCommand::ScriptObjectSnapshotCapture: {
+        int id = command.snapshotId();
+        QScriptObjectSnapshot *snap = backend->scriptObjectSnapshot(id);
+        Q_ASSERT(snap != 0);
+        QScriptDebuggerValue object = command.scriptValue();
+        Q_ASSERT(object.type() == QScriptDebuggerValue::ObjectValue);
+        QScriptEngine *engine = backend->engine();
+        QScriptValue realObject = object.toScriptValue(engine);
+        Q_ASSERT(realObject.isObject());
+        QScriptObjectSnapshot::Delta delta = snap->capture(realObject);
+        QScriptDebuggerObjectSnapshotDelta result;
+        result.removedProperties = delta.removedProperties;
+        bool didIgnoreExceptions = backend->ignoreExceptions();
+        backend->setIgnoreExceptions(true);
+        for (int i = 0; i < delta.changedProperties.size(); ++i) {
+            const QScriptValueProperty &src = delta.changedProperties.at(i);
+            bool hadException = engine->hasUncaughtException();
+            QString str = src.value().toString();
+            if (!hadException && engine->hasUncaughtException())
+                engine->clearExceptions();
+            QScriptDebuggerValueProperty dest(src.name(), src.value(), str, src.flags());
+            result.changedProperties.append(dest);
+        }
+        for (int j = 0; j < delta.addedProperties.size(); ++j) {
+            const QScriptValueProperty &src = delta.addedProperties.at(j);
+            bool hadException = engine->hasUncaughtException();
+            QString str = src.value().toString();
+            if (!hadException && engine->hasUncaughtException())
+                engine->clearExceptions();
+            QScriptDebuggerValueProperty dest(src.name(), src.value(), str, src.flags());
+            result.addedProperties.append(dest);
+        }
+        backend->setIgnoreExceptions(didIgnoreExceptions);
+        response.setResult(qVariantFromValue(result));
+    }   break;
+
+    case QScriptDebuggerCommand::DeleteScriptObjectSnapshot: {
+        int id = command.snapshotId();
+        backend->deleteScriptObjectSnapshot(id);
+    }   break;
+
+    case QScriptDebuggerCommand::NewScriptValueIterator: {
+        QScriptDebuggerValue object = command.scriptValue();
+        Q_ASSERT(object.type() == QScriptDebuggerValue::ObjectValue);
+        QScriptEngine *engine = backend->engine();
+        QScriptValue realObject = object.toScriptValue(engine);
+        Q_ASSERT(realObject.isObject());
+        int id = backend->newScriptValueIterator(realObject);
+        response.setResult(id);
+    }   break;
+
+    case QScriptDebuggerCommand::GetPropertiesByIterator: {
+        int id = command.iteratorId();
+        int count = 1000;
+        QScriptValueIterator *it = backend->scriptValueIterator(id);
+        Q_ASSERT(it != 0);
+        QScriptDebuggerValuePropertyList props;
+        for (int i = 0; (i < count) && it->hasNext(); ++i) {
+            it->next();
+            QString name = it->name();
+            QScriptValue value = it->value();
+            QString valueAsString = value.toString();
+            QScriptValue::PropertyFlags flags = it->flags();
+            QScriptDebuggerValueProperty prp(name, value, valueAsString, flags);
+            props.append(prp);
+        }
+        response.setResult(props);
+    }   break;
+
+    case QScriptDebuggerCommand::DeleteScriptValueIterator: {
+        int id = command.iteratorId();
+        backend->deleteScriptValueIterator(id);
+    }   break;
+
+    case QScriptDebuggerCommand::Evaluate: {
+        int contextIndex = command.contextIndex();
+        QString program = command.program();
+        QString fileName = command.fileName();
+        int lineNumber = command.lineNumber();
+        backend->evaluate(contextIndex, program, fileName, lineNumber);
+        response.setAsync(true);
+    }   break;
+
+    case QScriptDebuggerCommand::ScriptValueToString: {
+        QScriptDebuggerValue value = command.scriptValue();
+        QScriptEngine *engine = backend->engine();
+        QScriptValue realValue = value.toScriptValue(engine);
+        response.setResult(realValue.toString());
+    }   break;
+
+    case QScriptDebuggerCommand::SetScriptValueProperty: {
+        QScriptDebuggerValue object = command.scriptValue();
+        QScriptEngine *engine = backend->engine();
+        QScriptValue realObject = object.toScriptValue(engine);
+        QScriptDebuggerValue value = command.subordinateScriptValue();
+        QScriptValue realValue = value.toScriptValue(engine);
+        QString name = command.name();
+        realObject.setProperty(name, realValue);
+    }   break;
+
+    case QScriptDebuggerCommand::ClearExceptions:
+        backend->engine()->clearExceptions();
+        break;
+
+    case QScriptDebuggerCommand::UserCommand:
+    case QScriptDebuggerCommand::MaxUserCommand:
+        break;
+    }
+    return response;
+}
+
+QT_END_NAMESPACE