aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@me.com>2013-09-11 13:46:27 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-10-24 14:34:33 +0200
commit63ddfee7550b4d12ddb56e4575d8bb27c664c41e (patch)
tree4aa6281bec7705bc183829b1a776916d8b0054ce /src
parentaa1760f84bd711cf56159075630fb71d38a4087b (diff)
V4 debugger: retrieve formals and locals.
Change-Id: I47507a4d7d1b429b9c43ed3a7822079efe577327 Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src')
-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
9 files changed, 238 insertions, 101 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);