aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qqmlcodegenerator_p.h5
-rw-r--r--src/qml/compiler/qv4jsir_p.h6
-rw-r--r--src/qml/compiler/qv4ssa.cpp2
-rw-r--r--src/qml/jsruntime/qv4debugging.cpp281
-rw-r--r--src/qml/jsruntime/qv4debugging_p.h35
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4script.cpp4
-rw-r--r--src/qml/qml/qqmlcompiler.cpp2
-rw-r--r--src/qml/qml/qqmltypeloader.cpp2
-rw-r--r--tests/auto/qml/qv4debugger/tst_qv4debugger.cpp167
10 files changed, 404 insertions, 102 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h
index 8de08a81d1..1830b62772 100644
--- a/src/qml/compiler/qqmlcodegenerator_p.h
+++ b/src/qml/compiler/qqmlcodegenerator_p.h
@@ -166,8 +166,9 @@ struct Pragma
struct ParsedQML
{
- ParsedQML()
- : jsGenerator(&jsModule, sizeof(QV4::CompiledData::QmlUnit))
+ ParsedQML(bool debugMode)
+ : jsModule(debugMode)
+ , jsGenerator(&jsModule, sizeof(QV4::CompiledData::QmlUnit))
{}
QString code;
QQmlJS::Engine jsParserEngine;
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
index 7b0ee52737..8d090cab1e 100644
--- a/src/qml/compiler/qv4jsir_p.h
+++ b/src/qml/compiler/qv4jsir_p.h
@@ -683,12 +683,14 @@ struct Q_QML_EXPORT Module {
Function *rootFunction;
QString fileName;
bool isQmlModule; // implies rootFunction is always 0
+ bool debugMode;
Function *newFunction(const QString &name, Function *outer);
- Module()
+ Module(bool debugMode)
: rootFunction(0)
, isQmlModule(false)
+ , debugMode(debugMode)
{}
~Module();
@@ -764,7 +766,7 @@ struct Function {
int indexOfArgument(const QStringRef &string) const;
bool variablesCanEscape() const
- { return hasDirectEval || !nestedFunctions.isEmpty(); }
+ { return hasDirectEval || !nestedFunctions.isEmpty() || module->debugMode; }
};
struct BasicBlock {
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index c9ff6ad53c..d1ebbcc26b 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -2858,7 +2858,7 @@ void Optimizer::run()
static bool doSSA = qgetenv("QV4_NO_SSA").isEmpty();
static bool doOpt = qgetenv("QV4_NO_OPT").isEmpty();
- if (!function->hasTry && !function->hasWith && doSSA) {
+ if (!function->hasTry && !function->hasWith && !function->module->debugMode && doSSA) {
// qout << "SSA for " << *function->name << endl;
// qout << "Starting edge splitting..." << endl;
splitCriticalEdges(function);
diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp
index 41ed34ea18..a29aba8504 100644
--- a/src/qml/jsruntime/qv4debugging.cpp
+++ b/src/qml/jsruntime/qv4debugging.cpp
@@ -44,6 +44,7 @@
#include "qv4functionobject_p.h"
#include "qv4function_p.h"
#include "qv4instr_moth_p.h"
+#include "qv4runtime_p.h"
#include <iostream>
#include <algorithm>
@@ -52,7 +53,7 @@ using namespace QV4;
using namespace QV4::Debugging;
Debugger::Debugger(QV4::ExecutionEngine *engine)
- : _engine(engine)
+ : m_engine(engine)
, m_agent(0)
, m_state(Running)
, m_pauseRequested(false)
@@ -122,7 +123,7 @@ Debugger::ExecutionState Debugger::currentExecutionState(const uchar *code) cons
// ### Locking
ExecutionState state;
- QV4::ExecutionContext *context = _engine->current;
+ QV4::ExecutionContext *context = m_engine->current;
QV4::Function *function = 0;
if (CallContext *callCtx = context->asCallContext())
function = callCtx->function->function;
@@ -145,6 +146,187 @@ void Debugger::setPendingBreakpoints(Function *function)
m_pendingBreakPointsToAddToFutureCode.applyToFunction(function, /*removeBreakPoints*/ false);
}
+QVector<StackFrame> Debugger::stackTrace(int frameLimit) const
+{
+ return m_engine->stackTrace(frameLimit);
+}
+
+QList<Debugger::VarInfo> Debugger::retrieveFromValue(const ObjectRef o, const QStringList &path) const
+{
+ QList<Debugger::VarInfo> props;
+ if (!o)
+ return props;
+
+ Scope scope(m_engine);
+ ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
+ ScopedValue name(scope);
+ ScopedValue val(scope);
+ while (true) {
+ Value v;
+ name = it.nextPropertyNameAsString(&v);
+ if (name->isNull())
+ break;
+ QString key = name->toQStringNoThrow();
+ if (path.isEmpty()) {
+ val = v;
+ QVariant varValue;
+ VarInfo::Type type;
+ convert(val, &varValue, &type);
+ props.append(VarInfo(key, varValue, type));
+ } else if (path.first() == key) {
+ QStringList pathTail = path;
+ pathTail.pop_front();
+ return retrieveFromValue(ScopedObject(scope, v), pathTail);
+ }
+ }
+
+ return props;
+}
+
+void Debugger::convert(ValueRef v, QVariant *varValue, VarInfo::Type *type) const
+{
+ Q_ASSERT(varValue);
+ Q_ASSERT(type);
+
+ switch (v->type()) {
+ case Value::Empty_Type:
+ Q_ASSERT(!"empty Value encountered");
+ break;
+ case Value::Undefined_Type:
+ *type = VarInfo::Undefined;
+ varValue->setValue<int>(0);
+ break;
+ case Value::Null_Type:
+ *type = VarInfo::Null;
+ varValue->setValue<int>(0);
+ break;
+ case Value::Boolean_Type:
+ *type = VarInfo::Bool;
+ varValue->setValue<bool>(v->booleanValue());
+ break;
+ case Value::Managed_Type:
+ if (v->isString()) {
+ *type = VarInfo::String;
+ varValue->setValue<QString>(v->stringValue()->toQString());
+ } else {
+ *type = VarInfo::Object;
+ ExecutionContext *ctx = v->objectValue()->internalClass->engine->current;
+ Scope scope(ctx);
+ ScopedValue prim(scope, __qmljs_to_primitive(v, STRING_HINT));
+ varValue->setValue<QString>(prim->toQString());
+ }
+ break;
+ case Value::Integer_Type:
+ *type = VarInfo::Number;
+ varValue->setValue<double>((double)v->int_32);
+ break;
+ default: // double
+ *type = VarInfo::Number;
+ varValue->setValue<double>(v->doubleValue());
+ break;
+ }
+}
+
+static CallContext *findContext(ExecutionContext *ctxt, int frame)
+{
+ while (ctxt) {
+ if (CallContext *cCtxt = ctxt->asCallContext()) {
+ if (frame < 1)
+ return cCtxt;
+ --frame;
+ }
+ ctxt = ctxt->parent;
+ }
+
+ return 0;
+}
+
+/// Retrieves all arguments from a context, or all properties in an object passed in an argument.
+///
+/// \arg frame specifies the frame number: 0 is top of stack, 1 is the parent of the current frame, etc.
+/// \arg path when empty, retrieve all arguments in the specified frame. When not empty, find the
+/// argument with the same name as the first element in the path (in the specified frame, of
+/// course), and then use the rest of the path to walk nested objects. When the path is empty,
+/// retrieve all properties in that object. If an intermediate non-object is specified by the
+/// path, or non of the property names match, an empty list is returned.
+QList<Debugger::VarInfo> Debugger::retrieveArgumentsFromContext(const QStringList &path, int frame)
+{
+ QList<VarInfo> args;
+
+ if (state() != Paused)
+ return args;
+
+ if (frame < 0)
+ return args;
+
+ CallContext *ctxt = findContext(m_engine->current, frame);
+ if (!ctxt)
+ return args;
+
+ Scope scope(m_engine);
+ ScopedValue v(scope);
+ for (unsigned i = 0, ei = ctxt->formalCount(); i != ei; ++i) {
+ // value = ctxt->argument(i);
+ String *name = ctxt->formals()[i];
+ QString qName;
+ if (name)
+ qName = name->toQString();
+ if (path.isEmpty()) {
+ v = ctxt->argument(i);
+ QVariant value;
+ VarInfo::Type type;
+ convert(v, &value, &type);
+ args.append(VarInfo(qName, value, type));
+ } else if (path.first() == qName) {
+ ScopedObject o(scope, ctxt->argument(i));
+ QStringList pathTail = path;
+ pathTail.pop_front();
+ return retrieveFromValue(o, pathTail);
+ }
+ }
+
+ return args;
+}
+
+/// Same as \c retrieveArgumentsFromContext, but now for locals.
+QList<Debugger::VarInfo> Debugger::retrieveLocalsFromContext(const QStringList &path, int frame)
+{
+ QList<VarInfo> args;
+
+ if (state() != Paused)
+ return args;
+
+ if (frame < 0)
+ return args;
+
+ CallContext *ctxt = findContext(m_engine->current, frame);
+ if (!ctxt)
+ return args;
+
+ Scope scope(m_engine);
+ ScopedValue v(scope);
+ for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) {
+ String *name = ctxt->variables()[i];
+ QString qName;
+ if (name)
+ qName = name->toQString();
+ if (path.isEmpty()) {
+ v = ctxt->locals[i];
+ QVariant value;
+ VarInfo::Type type;
+ convert(v, &value, &type);
+ args.append(VarInfo(qName, value, type));
+ } else if (path.first() == qName) {
+ ScopedObject o(scope, ctxt->locals[i]);
+ QStringList pathTail = path;
+ pathTail.pop_front();
+ return retrieveFromValue(o, pathTail);
+ }
+ }
+
+ return args;
+}
+
void Debugger::maybeBreakAtInstruction(const uchar *code, bool breakPointHit)
{
QMutexLocker locker(&m_lock);
@@ -187,7 +369,7 @@ void Debugger::pauseAndWait()
void Debugger::applyPendingBreakPoints()
{
- foreach (QV4::CompiledData::CompilationUnit *unit, _engine->compilationUnits) {
+ foreach (QV4::CompiledData::CompilationUnit *unit, m_engine->compilationUnits) {
foreach (Function *function, unit->runtimeFunctions) {
m_pendingBreakPointsToAdd.applyToFunction(function, /*removeBreakPoints*/false);
m_pendingBreakPointsToRemove.applyToFunction(function, /*removeBreakPoints*/true);
@@ -205,92 +387,6 @@ void Debugger::applyPendingBreakPoints()
m_havePendingBreakPoints = false;
}
-static void realDumpValue(const QV4::ValueRef v, QV4::ExecutionContext *ctx, std::string prefix)
-{
- using namespace QV4;
- using namespace std;
-
- Scope scope(ctx);
-
- cout << prefix << "tag: " << hex << v->tag << dec << endl << prefix << "\t-> ";
- switch (v->type()) {
- case Value::Undefined_Type: cout << "Undefined"; return;
- case Value::Null_Type: cout << "Null"; return;
- case Value::Boolean_Type: cout << "Boolean"; break;
- case Value::Integer_Type: cout << "Integer"; break;
- case Value::Managed_Type: cout << v->managed()->className().toUtf8().data(); break;
- default: cout << "UNKNOWN" << endl; return;
- }
- cout << endl;
-
- if (v->isBoolean()) {
- cout << prefix << "\t-> " << (v->booleanValue() ? "TRUE" : "FALSE") << endl;
- return;
- }
-
- if (v->isInteger()) {
- cout << prefix << "\t-> " << v->integerValue() << endl;
- return;
- }
-
- if (v->isDouble()) {
- cout << prefix << "\t-> " << v->doubleValue() << endl;
- return;
- }
-
- if (v->isString()) {
- // maybe check something on the Managed object?
- cout << prefix << "\t-> @" << hex << v->stringValue() << endl;
- cout << prefix << "\t-> \"" << qPrintable(v->stringValue()->toQString()) << "\"" << endl;
- return;
- }
-
- ScopedObject o(scope, v);
- if (!o)
- return;
-
- cout << prefix << "\t-> @" << hex << o << endl;
- cout << prefix << "object type: " << o->internalType() << endl << prefix << "\t-> ";
- switch (o->internalType()) {
- case QV4::Managed::Type_Invalid: cout << "Invalid"; break;
- case QV4::Managed::Type_String: cout << "String"; break;
- case QV4::Managed::Type_Object: cout << "Object"; break;
- case QV4::Managed::Type_ArrayObject: cout << "ArrayObject"; break;
- case QV4::Managed::Type_FunctionObject: cout << "FunctionObject"; break;
- case QV4::Managed::Type_BooleanObject: cout << "BooleanObject"; break;
- case QV4::Managed::Type_NumberObject: cout << "NumberObject"; break;
- case QV4::Managed::Type_StringObject: cout << "StringObject"; break;
- case QV4::Managed::Type_DateObject: cout << "DateObject"; break;
- case QV4::Managed::Type_RegExpObject: cout << "RegExpObject"; break;
- case QV4::Managed::Type_ErrorObject: cout << "ErrorObject"; break;
- case QV4::Managed::Type_ArgumentsObject: cout << "ArgumentsObject"; break;
- case QV4::Managed::Type_JSONObject: cout << "JSONObject"; break;
- case QV4::Managed::Type_MathObject: cout << "MathObject"; break;
- case QV4::Managed::Type_ForeachIteratorObject: cout << "ForeachIteratorObject"; break;
- default: cout << "UNKNOWN" << endl; return;
- }
- cout << endl;
-
- cout << prefix << "properties:" << endl;
- ForEachIteratorObject it(ctx, o);
- ScopedValue name(scope);
- ScopedValue pval(scope);
- for (name = it.nextPropertyName(); !name->isNull(); name = it.nextPropertyName()) {
- cout << prefix << "\t\"" << qPrintable(name->stringValue()->toQString()) << "\"" << endl;
- PropertyAttributes attrs;
- Property *d = o->__getOwnProperty__(ScopedString(scope, name), &attrs);
- pval = o->getValue(d, attrs);
- cout << prefix << "\tvalue:" << endl;
- realDumpValue(pval, ctx, prefix + "\t");
- }
-}
-
-void dumpValue(const QV4::ValueRef v, QV4::ExecutionContext *ctx)
-{
- realDumpValue(v, ctx, std::string(""));
-}
-
-
void DebuggerAgent::addDebugger(Debugger *debugger)
{
Q_ASSERT(!m_debuggers.contains(debugger));
@@ -315,6 +411,13 @@ void DebuggerAgent::pauseAll() const
pause(debugger);
}
+void DebuggerAgent::resumeAll() const
+{
+ foreach (Debugger *debugger, m_debuggers)
+ if (debugger->state() == Debugger::Paused)
+ debugger->resume();
+}
+
void DebuggerAgent::addBreakPoint(const QString &fileName, int lineNumber) const
{
foreach (Debugger *debugger, m_debuggers)
diff --git a/src/qml/jsruntime/qv4debugging_p.h b/src/qml/jsruntime/qv4debugging_p.h
index e44f415da4..133cc3e17c 100644
--- a/src/qml/jsruntime/qv4debugging_p.h
+++ b/src/qml/jsruntime/qv4debugging_p.h
@@ -64,12 +64,35 @@ class DebuggerAgent;
class Q_QML_EXPORT Debugger
{
public:
+ struct VarInfo {
+ enum Type {
+ Invalid = 0,
+ Undefined = 1,
+ Null,
+ Number,
+ String,
+ Bool,
+ Object
+ };
+
+ QString name;
+ QVariant value;
+ Type type;
+
+ VarInfo(): type(Invalid) {}
+ VarInfo(const QString &name, const QVariant &value, Type type)
+ : name(name), value(value), type(type)
+ {}
+
+ bool isValid() const { return type != Invalid; }
+ };
+
enum State {
Running,
Paused
};
- Debugger(ExecutionEngine *_engine);
+ Debugger(ExecutionEngine *engine);
~Debugger();
void attachToAgent(DebuggerAgent *agent);
@@ -98,6 +121,10 @@ public:
}
void setPendingBreakpoints(Function *function);
+ QVector<StackFrame> stackTrace(int frameLimit = -1) const;
+ QList<VarInfo> retrieveArgumentsFromContext(const QStringList &path, int frame = 0);
+ QList<VarInfo> retrieveLocalsFromContext(const QStringList &path, int frame = 0);
+
public: // compile-time interface
void maybeBreakAtInstruction(const uchar *code, bool breakPointHit);
@@ -110,6 +137,9 @@ private:
void applyPendingBreakPoints();
+ QList<Debugger::VarInfo> retrieveFromValue(const ObjectRef o, const QStringList &path) const;
+ void convert(ValueRef v, QVariant *varValue, VarInfo::Type *type) const;
+
struct BreakPoints : public QHash<QString, QList<int> >
{
void add(const QString &fileName, int lineNumber);
@@ -118,7 +148,7 @@ private:
void applyToFunction(Function *function, bool removeBreakPoints);
};
- QV4::ExecutionEngine *_engine;
+ QV4::ExecutionEngine *m_engine;
DebuggerAgent *m_agent;
QMutex m_lock;
QWaitCondition m_runningCondition;
@@ -142,6 +172,7 @@ public:
void pause(Debugger *debugger) const;
void pauseAll() const;
+ void resumeAll() const;
void addBreakPoint(const QString &fileName, int lineNumber) const;
void removeBreakPoint(const QString &fileName, int lineNumber) const;
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 55baef06db..3e28024bbf 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -267,7 +267,7 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData)
if (!fe)
v4->current->throwSyntaxError(0);
- QQmlJS::V4IR::Module module;
+ QQmlJS::V4IR::Module module(v4->debugger != 0);
QQmlJS::RuntimeCodegen cg(v4->current, f->strictMode);
cg.generateFromFunctionExpression(QString(), function, fe, &module);
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index d30132140b..daca700609 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -173,7 +173,7 @@ void Script::parse()
MemoryManager::GCBlocker gcBlocker(v4->memoryManager);
- V4IR::Module module;
+ V4IR::Module module(v4->debugger != 0);
QQmlJS::Engine ee, *engine = &ee;
Lexer lexer(engine);
@@ -308,7 +308,7 @@ CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const
using namespace QQmlJS;
using namespace QQmlJS::AST;
- QQmlJS::V4IR::Module module;
+ QQmlJS::V4IR::Module module(engine->debugger != 0);
QQmlJS::Engine ee;
QQmlJS::Lexer lexer(&ee);
diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp
index 2cfb074aae..57114ebead 100644
--- a/src/qml/qml/qqmlcompiler.cpp
+++ b/src/qml/qml/qqmlcompiler.cpp
@@ -810,7 +810,7 @@ bool QQmlCompiler::compile(QQmlEngine *engine,
this->unit = unit;
this->unitRoot = root;
this->output = out;
- this->jsModule.reset(new QQmlJS::V4IR::Module);
+ this->jsModule.reset(new QQmlJS::V4IR::Module(enginePrivate->v4engine()->debugger));
this->jsModule->isQmlModule = true;
// Compile types
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 601c1b8bdc..d7ba157f94 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -2141,7 +2141,7 @@ void QQmlTypeData::dataReceived(const Data &data)
if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse"));
if (m_useNewCompiler) {
- parsedQML.reset(new QtQml::ParsedQML);
+ parsedQML.reset(new QtQml::ParsedQML(QV8Engine::getV4(typeLoader()->engine())->debugger != 0));
QQmlCodeGenerator compiler;
if (!compiler.generateFromQml(code, finalUrl(), finalUrlString(), parsedQML.data())) {
setError(compiler.errors);
diff --git a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
index 9fbcb4a118..27091e55ca 100644
--- a/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/qv4debugger/tst_qv4debugger.cpp
@@ -99,12 +99,15 @@ class TestAgent : public QV4::Debugging::DebuggerAgent
{
Q_OBJECT
public:
+ typedef QV4::Debugging::Debugger Debugger;
+
TestAgent()
: m_wasPaused(false)
+ , m_captureContextInfo(false)
{
}
- virtual void debuggerPaused(QV4::Debugging::Debugger *debugger)
+ virtual void debuggerPaused(Debugger *debugger)
{
Q_ASSERT(m_debuggers.count() == 1 && m_debuggers.first() == debugger);
m_wasPaused = true;
@@ -114,6 +117,9 @@ public:
debugger->addBreakPoint(bp.fileName, bp.lineNumber);
m_breakPointsToAddWhenPaused.clear();
+ if (m_captureContextInfo)
+ captureContextInfo(debugger);
+
debugger->resume();
}
@@ -128,18 +134,49 @@ public:
int lineNumber;
};
+ void captureContextInfo(Debugger *debugger)
+ {
+ m_stackTrace = debugger->stackTrace();
+ for (int i = 0, ei = m_stackTrace.size(); i != ei; ++i) {
+ m_capturedArguments.append(debugger->retrieveArgumentsFromContext(QStringList(), i));
+ m_capturedLocals.append(debugger->retrieveLocalsFromContext(QStringList(), i));
+ }
+
+ foreach (const QStringList &path, m_localPathsToRead)
+ m_localPathResults += debugger->retrieveLocalsFromContext(path);
+ }
+
bool m_wasPaused;
+ bool m_captureContextInfo;
QList<QV4::Debugging::Debugger::ExecutionState> m_statesWhenPaused;
QList<TestBreakPoint> m_breakPointsToAddWhenPaused;
+ QVector<QV4::StackFrame> m_stackTrace;
+ QList<QList<Debugger::VarInfo> > m_capturedArguments;
+ QList<QList<Debugger::VarInfo> > m_capturedLocals;
+ QList<QStringList> m_localPathsToRead;
+ QList<QList<Debugger::VarInfo> > m_localPathResults;
+
+ // Utility methods:
+ void dumpStackTrace() const
+ {
+ qDebug() << "Stack depth:" << m_stackTrace.size();
+ foreach (const QV4::StackFrame &frame, m_stackTrace)
+ qDebug("\t%s (%s:%d:%d)", qPrintable(frame.function), qPrintable(frame.source),
+ frame.line, frame.column);
+ }
};
class tst_qv4debugger : public QObject
{
Q_OBJECT
+
+ typedef QV4::Debugging::Debugger::VarInfo VarInfo;
+
private slots:
void init();
void cleanup();
+ // breakpoints:
void breakAnywhere();
void pendingBreakpoint();
void liveBreakPoint();
@@ -147,6 +184,12 @@ private slots:
void addBreakPointWhilePaused();
void removeBreakPointForNextInstruction();
+ // context access:
+ void readArguments();
+ void readLocals();
+ void readObject();
+ void readContextInAllFrames();
+
private:
void evaluateJavaScript(const QString &script, const QString &fileName, int lineNumber = 1)
{
@@ -283,6 +326,128 @@ void tst_qv4debugger::removeBreakPointForNextInstruction()
QVERIFY(!m_debuggerAgent->m_wasPaused);
}
+void tst_qv4debugger::readArguments()
+{
+ m_debuggerAgent->m_captureContextInfo = true;
+ QString script =
+ "function f(a, b, c, d) {\n"
+ " return a === b\n"
+ "}\n"
+ "var four;\n"
+ "f(1, 'two', null, four);\n";
+ m_debuggerAgent->addBreakPoint("readArguments", 2);
+ evaluateJavaScript(script, "readArguments");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0].size(), 4);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][0].name, QString("a"));
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][0].type, VarInfo::Number);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][0].value.toDouble(), 1.0);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][1].name, QString("b"));
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][1].type, VarInfo::String);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][1].value.toString(), QLatin1String("two"));
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][2].name, QString("c"));
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][2].type, VarInfo::Null);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][3].name, QString("d"));
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[0][3].type, VarInfo::Undefined);
+}
+
+void tst_qv4debugger::readLocals()
+{
+ m_debuggerAgent->m_captureContextInfo = true;
+ QString script =
+ "function f(a, b) {\n"
+ " var c = a + b\n"
+ " var d = a - b\n" // breakpoint, c should be set, d should be undefined
+ " return c === d\n"
+ "}\n"
+ "f(1, 2, 3);\n";
+ m_debuggerAgent->addBreakPoint("readLocals", 3);
+ evaluateJavaScript(script, "readLocals");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0].size(), 2);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][0].name, QString("c"));
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][0].type, VarInfo::Number);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][0].value.toDouble(), 3.0);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][1].name, QString("d"));
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][1].type, VarInfo::Undefined);
+}
+
+void tst_qv4debugger::readObject()
+{
+ m_debuggerAgent->m_captureContextInfo = true;
+ QString script =
+ "function f(a) {\n"
+ " var b = a\n"
+ " return b\n"
+ "}\n"
+ "f({head: 1, tail: { head: 'asdf', tail: null }});\n";
+ m_debuggerAgent->addBreakPoint("readObject", 3);
+ m_debuggerAgent->m_localPathsToRead.append(QStringList() << QLatin1String("b"));
+ m_debuggerAgent->m_localPathsToRead.append(QStringList() << QLatin1String("b") << QLatin1String("tail"));
+ evaluateJavaScript(script, "readObject");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0].size(), 1);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][0].name, QString("b"));
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[0][0].type, VarInfo::Object);
+
+ QCOMPARE(m_debuggerAgent->m_localPathResults.size(), 2);
+
+ QList<VarInfo> b = m_debuggerAgent->m_localPathResults[0];
+ QCOMPARE(b.size(), 2);
+ QCOMPARE(b[0].name, QLatin1String("head"));
+ QCOMPARE(b[0].type, VarInfo::Number);
+ QCOMPARE(b[0].value.toDouble(), 1.0);
+ QCOMPARE(b[1].name, QLatin1String("tail"));
+ QCOMPARE(b[1].type, VarInfo::Object);
+
+ QList<VarInfo> b_tail = m_debuggerAgent->m_localPathResults[1];
+ QCOMPARE(b_tail.size(), 2);
+ QCOMPARE(b_tail[0].name, QLatin1String("head"));
+ QCOMPARE(b_tail[0].type, VarInfo::String);
+ QCOMPARE(b_tail[0].value.toString(), QLatin1String("asdf"));
+ QCOMPARE(b_tail[1].name, QLatin1String("tail"));
+ QCOMPARE(b_tail[1].type, VarInfo::Null);
+}
+
+void tst_qv4debugger::readContextInAllFrames()
+{
+ m_debuggerAgent->m_captureContextInfo = true;
+ QString script =
+ "function fact(n) {\n"
+ " if (n > 1) {\n"
+ " var n_1 = n - 1;\n"
+ " n_1 = fact(n_1);\n"
+ " return n * n_1;\n"
+ " } else\n"
+ " return 1;\n" // breakpoint
+ "}\n"
+ "fact(12);\n";
+ m_debuggerAgent->addBreakPoint("readFormalsInAllFrames", 7);
+ evaluateJavaScript(script, "readFormalsInAllFrames");
+ QVERIFY(m_debuggerAgent->m_wasPaused);
+ QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 13);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments.size(), 13);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals.size(), 13);
+
+ for (int i = 0; i < 12; ++i) {
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[i].size(), 1);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[i][0].name, QString("n"));
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[i][0].type, VarInfo::Number);
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[i][0].value.toInt(), i + 1);
+
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[i].size(), 1);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[i][0].name, QString("n_1"));
+ if (i == 0) {
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[i][0].type, VarInfo::Undefined);
+ } else {
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[i][0].type, VarInfo::Number);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[i][0].value.toInt(), i);
+ }
+ }
+ QCOMPARE(m_debuggerAgent->m_capturedArguments[12].size(), 0);
+ QCOMPARE(m_debuggerAgent->m_capturedLocals[12].size(), 0);
+}
+
QTEST_MAIN(tst_qv4debugger)
#include "tst_qv4debugger.moc"