summaryrefslogtreecommitdiff
path: root/src/qml/jsruntime/qv4debugging.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4debugging.cpp')
-rw-r--r--src/qml/jsruntime/qv4debugging.cpp594
1 files changed, 31 insertions, 563 deletions
diff --git a/src/qml/jsruntime/qv4debugging.cpp b/src/qml/jsruntime/qv4debugging.cpp
index 6f3e48352e..ceeef80b9f 100644
--- a/src/qml/jsruntime/qv4debugging.cpp
+++ b/src/qml/jsruntime/qv4debugging.cpp
@@ -54,60 +54,49 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
using namespace QV4::Debugging;
-namespace {
-class JavaScriptJob: public Debugger::Job
-{
- QV4::ExecutionEngine *engine;
- int frameNr;
- const QString &script;
-
-public:
- JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, const QString &script)
- : engine(engine)
- , frameNr(frameNr)
- , script(script)
- {}
+Debugger::JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr,
+ const QString &script)
+ : engine(engine)
+ , frameNr(frameNr)
+ , script(script)
+{}
- void run()
- {
- Scope scope(engine);
+void Debugger::JavaScriptJob::run()
+{
+ Scope scope(engine);
- ExecutionContextSaver saver(scope, engine->currentContext());
+ ExecutionContextSaver saver(scope, engine->currentContext());
- if (frameNr > 0) {
- Value *savedContexts = scope.alloc(frameNr);
- for (int i = 0; i < frameNr; ++i) {
- savedContexts[i] = engine->currentContext();
- engine->popContext();
- }
+ if (frameNr > 0) {
+ Value *savedContexts = scope.alloc(frameNr);
+ for (int i = 0; i < frameNr; ++i) {
+ savedContexts[i] = engine->currentContext();
+ engine->popContext();
}
-
- ScopedContext ctx(scope, engine->currentContext());
- QV4::Script script(ctx, this->script);
- script.strictMode = ctx->d()->strictMode;
- // In order for property lookups in QML to work, we need to disable fast v4 lookups. That
- // is a side-effect of inheritContext.
- script.inheritContext = true;
- script.parse();
- QV4::ScopedValue result(scope);
- if (!scope.engine->hasException)
- result = script.run();
- if (scope.engine->hasException)
- result = scope.engine->catchException();
- handleResult(result);
}
-protected:
- virtual void handleResult(QV4::ScopedValue &result) = 0;
-};
+ ScopedContext ctx(scope, engine->currentContext());
+ QV4::Script script(ctx, this->script);
+ script.strictMode = ctx->d()->strictMode;
+ // In order for property lookups in QML to work, we need to disable fast v4 lookups. That
+ // is a side-effect of inheritContext.
+ script.inheritContext = true;
+ script.parse();
+ QV4::ScopedValue result(scope);
+ if (!scope.engine->hasException)
+ result = script.run();
+ if (scope.engine->hasException)
+ result = scope.engine->catchException();
+ handleResult(result);
+}
-class EvalJob: public JavaScriptJob
+class EvalJob: public Debugger::JavaScriptJob
{
bool result;
public:
EvalJob(QV4::ExecutionEngine *engine, const QString &script)
- : JavaScriptJob(engine, /*frameNr*/-1, script)
+ : Debugger::JavaScriptJob(engine, /*frameNr*/-1, script)
, result(false)
{}
@@ -122,265 +111,6 @@ public:
}
};
-class ExpressionEvalJob: public JavaScriptJob
-{
- QV4::Debugging::DataCollector *collector;
-
-public:
- ExpressionEvalJob(ExecutionEngine *engine, int frameNr, const QString &expression,
- QV4::Debugging::DataCollector *collector)
- : JavaScriptJob(engine, frameNr, expression)
- , collector(collector)
- {
- }
-
- virtual void handleResult(QV4::ScopedValue &result)
- {
- collector->collect(result);
- }
-};
-
-class GatherSourcesJob: public Debugger::Job
-{
- QV4::ExecutionEngine *engine;
- const int seq;
-
-public:
- GatherSourcesJob(QV4::ExecutionEngine *engine, int seq)
- : engine(engine)
- , seq(seq)
- {}
-
- ~GatherSourcesJob() {}
-
- void run()
- {
- QStringList sources;
-
- foreach (QV4::CompiledData::CompilationUnit *unit, engine->compilationUnits) {
- QString fileName = unit->fileName();
- if (!fileName.isEmpty())
- sources.append(fileName);
- }
-
- Debugger *debugger = engine->debugger;
- emit debugger->sourcesCollected(debugger, sources, seq);
- }
-};
-}
-
-
-DataCollector::DataCollector(QV4::ExecutionEngine *engine)
- : m_engine(engine), m_collectedRefs(Q_NULLPTR)
-{
- values.set(engine, engine->newArrayObject());
-}
-
-DataCollector::~DataCollector()
-{
-}
-
-void DataCollector::collect(const ScopedValue &value)
-{
- if (m_collectedRefs)
- m_collectedRefs->append(addRef(value));
-}
-
-QJsonObject DataCollector::lookupRef(Ref ref)
-{
- QJsonObject dict;
- if (lookupSpecialRef(ref, &dict))
- return dict;
-
- dict.insert(QStringLiteral("handle"), qint64(ref));
-
- QV4::Scope scope(engine());
- QV4::ScopedValue value(scope, getValue(ref));
- switch (value->type()) {
- case QV4::Value::Empty_Type:
- Q_ASSERT(!"empty Value encountered");
- break;
- case QV4::Value::Undefined_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("undefined"));
- break;
- case QV4::Value::Null_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("null"));
- break;
- case QV4::Value::Boolean_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("boolean"));
- dict.insert(QStringLiteral("value"), value->booleanValue() ? QStringLiteral("true")
- : QStringLiteral("false"));
- break;
- case QV4::Value::Managed_Type:
- if (QV4::String *s = value->as<QV4::String>()) {
- dict.insert(QStringLiteral("type"), QStringLiteral("string"));
- dict.insert(QStringLiteral("value"), s->toQString());
- } else if (QV4::Object *o = value->as<QV4::Object>()) {
- dict.insert(QStringLiteral("type"), QStringLiteral("object"));
- dict.insert(QStringLiteral("properties"), collectProperties(o));
- } else {
- Q_UNREACHABLE();
- }
- break;
- case QV4::Value::Integer_Type:
- dict.insert(QStringLiteral("type"), QStringLiteral("number"));
- dict.insert(QStringLiteral("value"), value->integerValue());
- break;
- default: // double
- dict.insert(QStringLiteral("type"), QStringLiteral("number"));
- dict.insert(QStringLiteral("value"), value->doubleValue());
- break;
- }
-
- return dict;
-}
-
-DataCollector::Ref DataCollector::addFunctionRef(const QString &functionName)
-{
- Ref ref = addRef(QV4::Primitive::emptyValue(), false);
-
- QJsonObject dict;
- dict.insert(QStringLiteral("handle"), qint64(ref));
- dict.insert(QStringLiteral("type"), QStringLiteral("function"));
- dict.insert(QStringLiteral("className"), QStringLiteral("Function"));
- dict.insert(QStringLiteral("name"), functionName);
- specialRefs.insert(ref, dict);
-
- return ref;
-}
-
-DataCollector::Ref DataCollector::addScriptRef(const QString &scriptName)
-{
- Ref ref = addRef(QV4::Primitive::emptyValue(), false);
-
- QJsonObject dict;
- dict.insert(QStringLiteral("handle"), qint64(ref));
- dict.insert(QStringLiteral("type"), QStringLiteral("script"));
- dict.insert(QStringLiteral("name"), scriptName);
- specialRefs.insert(ref, dict);
-
- return ref;
-}
-
-void DataCollector::collectScope(QJsonObject *dict, Debugger *debugger, int frameNr, int scopeNr)
-{
- QStringList names;
-
- Refs refs;
- {
- RefHolder holder(this, &refs);
- debugger->collectArgumentsInContext(this, &names, frameNr, scopeNr);
- debugger->collectLocalsInContext(this, &names, frameNr, scopeNr);
- }
-
- QV4::Scope scope(engine());
- QV4::ScopedObject scopeObject(scope, engine()->newObject());
-
- Q_ASSERT(names.size() == refs.size());
- for (int i = 0, ei = refs.size(); i != ei; ++i)
- scopeObject->put(engine(), names.at(i),
- QV4::Value::fromReturnedValue(getValue(refs.at(i))));
-
- Ref scopeObjectRef = addRef(scopeObject);
- dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef));
- if (m_collectedRefs)
- m_collectedRefs->append(scopeObjectRef);
-}
-
-DataCollector::Ref DataCollector::addRef(Value value, bool deduplicate)
-{
- class ExceptionStateSaver
- {
- quint32 *hasExceptionLoc;
- quint32 hadException;
-
- public:
- ExceptionStateSaver(QV4::ExecutionEngine *engine)
- : hasExceptionLoc(&engine->hasException)
- , hadException(false)
- { std::swap(*hasExceptionLoc, hadException); }
-
- ~ExceptionStateSaver()
- { std::swap(*hasExceptionLoc, hadException); }
- };
-
- // if we wouldn't do this, the putIndexed won't work.
- ExceptionStateSaver resetExceptionState(engine());
- QV4::Scope scope(engine());
- QV4::ScopedObject array(scope, values.value());
- if (deduplicate) {
- for (Ref i = 0; i < array->getLength(); ++i) {
- if (array->getIndexed(i) == value.rawValue())
- return i;
- }
- }
- Ref ref = array->getLength();
- array->putIndexed(ref, value);
- Q_ASSERT(array->getLength() - 1 == ref);
- return ref;
-}
-
-ReturnedValue DataCollector::getValue(Ref ref)
-{
- QV4::Scope scope(engine());
- QV4::ScopedObject array(scope, values.value());
- Q_ASSERT(ref < array->getLength());
- return array->getIndexed(ref, Q_NULLPTR);
-}
-
-bool DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict)
-{
- SpecialRefs::const_iterator it = specialRefs.find(ref);
- if (it == specialRefs.end())
- return false;
-
- *dict = it.value();
- return true;
-}
-
-QJsonArray DataCollector::collectProperties(Object *object)
-{
- QJsonArray res;
-
- QV4::Scope scope(engine());
- QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
- QV4::ScopedValue name(scope);
- QV4::ScopedValue value(scope);
- while (true) {
- QV4::Value v;
- name = it.nextPropertyNameAsString(&v);
- if (name->isNull())
- break;
- QString key = name->toQStringNoThrow();
- value = v;
- res.append(collectAsJson(key, value));
- }
-
- return res;
-}
-
-QJsonObject DataCollector::collectAsJson(const QString &name, const ScopedValue &value)
-{
- QJsonObject dict;
- if (!name.isNull())
- dict.insert(QStringLiteral("name"), name);
- Ref ref = addRef(value);
- dict.insert(QStringLiteral("ref"), qint64(ref));
- if (m_collectedRefs)
- m_collectedRefs->append(ref);
-
- // TODO: enable this when creator can handle it.
- if (false) {
- if (value->isManaged() && !value->isString()) {
- QV4::Scope scope(engine());
- QV4::ScopedObject obj(scope, value->as<QV4::Object>());
- dict.insert(QStringLiteral("propertycount"), qint64(obj->getLength()));
- }
- }
-
- return dict;
-}
-
Debugger::Debugger(QV4::ExecutionEngine *engine)
: m_engine(engine)
, m_state(Running)
@@ -396,18 +126,6 @@ Debugger::Debugger(QV4::ExecutionEngine *engine)
qMetaTypeId<PauseReason>();
}
-void Debugger::gatherSources(int requestSequenceNr)
-{
- QMutexLocker locker(&m_lock);
-
- m_gatherSources = new GatherSourcesJob(m_engine, requestSequenceNr);
- if (m_state == Paused) {
- runInEngine_havingLock(m_gatherSources);
- delete m_gatherSources;
- m_gatherSources = 0;
- }
-}
-
void Debugger::pause()
{
QMutexLocker locker(&m_lock);
@@ -465,256 +183,6 @@ QVector<StackFrame> Debugger::stackTrace(int frameLimit) const
return m_engine->stackTrace(frameLimit);
}
-static inline Heap::CallContext *findContext(Heap::ExecutionContext *ctxt, int frame)
-{
- if (!ctxt)
- return 0;
-
- Scope scope(ctxt->engine);
- ScopedContext ctx(scope, ctxt);
- while (ctx) {
- CallContext *cCtxt = ctx->asCallContext();
- if (cCtxt && cCtxt->d()->function) {
- if (frame < 1)
- return cCtxt->d();
- --frame;
- }
- ctx = ctx->d()->parent;
- }
-
- return 0;
-}
-
-static inline Heap::CallContext *findScope(Heap::ExecutionContext *ctxt, int scope)
-{
- if (!ctxt)
- return 0;
-
- Scope s(ctxt->engine);
- ScopedContext ctx(s, ctxt);
- for (; scope > 0 && ctx; --scope)
- ctx = ctx->d()->outer;
-
- return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0;
-}
-
-void Debugger::collectArgumentsInContext(DataCollector *collector, QStringList *names, int frameNr,
- int scopeNr)
-{
- if (state() != Paused)
- return;
-
- class ArgumentCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- QV4::Debugging::DataCollector *collector;
- QStringList *names;
- int frameNr;
- int scopeNr;
-
- public:
- ArgumentCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector,
- QStringList *names, int frameNr, int scopeNr)
- : engine(engine)
- , collector(collector)
- , names(names)
- , frameNr(frameNr)
- , scopeNr(scopeNr)
- {}
-
- ~ArgumentCollectJob() {}
-
- void run()
- {
- if (frameNr < 0)
- return;
-
- Scope scope(engine);
- Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr));
- if (!ctxt)
- return;
-
- ScopedValue v(scope);
- int nFormals = ctxt->formalCount();
- for (unsigned i = 0, ei = nFormals; i != ei; ++i) {
- QString qName;
- if (Identifier *name = ctxt->formals()[nFormals - i - 1])
- qName = name->string;
- names->append(qName);
- v = ctxt->argument(i);
- collector->collect(v);
- }
- }
- };
-
- ArgumentCollectJob job(m_engine, collector, names, frameNr, scopeNr);
- runInEngine(&job);
-}
-
-/// Same as \c retrieveArgumentsFromContext, but now for locals.
-void Debugger::collectLocalsInContext(DataCollector *collector, QStringList *names, int frameNr,
- int scopeNr)
-{
- Q_ASSERT(collector);
- Q_ASSERT(names);
-
- if (state() != Paused)
- return;
-
- class LocalCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- DataCollector *collector;
- QStringList *names;
- int frameNr;
- int scopeNr;
-
- public:
- LocalCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector, QStringList *names, int frameNr, int scopeNr)
- : engine(engine)
- , collector(collector)
- , names(names)
- , frameNr(frameNr)
- , scopeNr(scopeNr)
- {}
-
- void run()
- {
- if (frameNr < 0)
- return;
-
- Scope scope(engine);
- Scoped<CallContext> ctxt(scope, findScope(findContext(engine->currentContext(), frameNr), scopeNr));
- if (!ctxt)
- return;
-
- ScopedValue v(scope);
- for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) {
- QString qName;
- if (Identifier *name = ctxt->variables()[i])
- qName = name->string;
- names->append(qName);
- v = ctxt->d()->locals[i];
- collector->collect(v);
- }
- }
- };
-
- LocalCollectJob job(m_engine, collector, names, frameNr, scopeNr);
- runInEngine(&job);
-}
-
-bool Debugger::collectThisInContext(DataCollector *collector, int frame)
-{
- if (state() != Paused)
- return false;
-
- class ThisCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- DataCollector *collector;
- int frameNr;
- bool *foundThis;
-
- public:
- ThisCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector, int frameNr,
- bool *foundThis)
- : engine(engine)
- , collector(collector)
- , frameNr(frameNr)
- , foundThis(foundThis)
- {}
-
- void run()
- {
- *foundThis = myRun();
- }
-
- bool myRun()
- {
- Scope scope(engine);
- ScopedContext ctxt(scope, findContext(engine->currentContext(), frameNr));
- while (ctxt) {
- if (CallContext *cCtxt = ctxt->asCallContext())
- if (cCtxt->d()->activation)
- break;
- ctxt = ctxt->d()->outer;
- }
-
- if (!ctxt)
- return false;
-
- ScopedValue o(scope, ctxt->asCallContext()->d()->activation);
- collector->collect(o);
- return true;
- }
- };
-
- bool foundThis = false;
- ThisCollectJob job(m_engine, collector, frame, &foundThis);
- runInEngine(&job);
- return foundThis;
-}
-
-bool Debugger::collectThrownValue(DataCollector *collector)
-{
- if (state() != Paused || !m_engine->hasException)
- return false;
-
- class ExceptionCollectJob: public Job
- {
- QV4::ExecutionEngine *engine;
- DataCollector *collector;
-
- public:
- ExceptionCollectJob(QV4::ExecutionEngine *engine, DataCollector *collector)
- : engine(engine)
- , collector(collector)
- {}
-
- void run()
- {
- Scope scope(engine);
- ScopedValue v(scope, *engine->exceptionValue);
- collector->collect(v);
- }
- };
-
- ExceptionCollectJob job(m_engine, collector);
- runInEngine(&job);
- return true;
-}
-
-QVector<Heap::ExecutionContext::ContextType> Debugger::getScopeTypes(int frame) const
-{
- QVector<Heap::ExecutionContext::ContextType> types;
-
- if (state() != Paused)
- return types;
-
- Scope scope(m_engine);
- Scoped<CallContext> sctxt(scope, findContext(m_engine->currentContext(), frame));
- if (!sctxt || sctxt->d()->type < Heap::ExecutionContext::Type_QmlContext)
- return types;
-
- ScopedContext it(scope, sctxt->d());
- for (; it; it = it->d()->outer)
- types.append(it->d()->type);
-
- return types;
-}
-
-
-void Debugger::evaluateExpression(int frameNr, const QString &expression,
- DataCollector *resultsCollector)
-{
- Q_ASSERT(state() == Paused);
-
- Q_ASSERT(m_runningJob == 0);
- ExpressionEvalJob job(m_engine, frameNr, expression, resultsCollector);
- runInEngine(&job);
-}
-
void Debugger::maybeBreakAtInstruction()
{
if (m_runningJob) // do not re-enter when we're doing a job for the debugger.