From c21bc1cdef5ae0f1e6bd43ac0a6c324a59d4e34b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 17 Aug 2015 16:54:36 +0200 Subject: V4 Debugger: Avoid special refs when looking up normal ones The "values" of "special" refs are always JavaScript "undefined"s. Thus, when trying to deduplicate an actual "undefined" we'd accidentally hit the special refs. Avoid this by checking if a found ref is special. In an ideal world we'd get rid of the special refs altogether and save QV4::FunctionObject in the values array, but that is not quite trivial as long as the QV4::ExecutionEngine::stackTrace() doesn't give us FunctionObjects. Task-number: QTBUG-47788 Change-Id: Idf358c285f40930220fad2207c6ab5c9101573b2 Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 64ee5c3b96..a44acdd370 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -236,7 +236,7 @@ QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicat QV4::ScopedObject array(scope, values.value()); if (deduplicate) { for (Ref i = 0; i < array->getLength(); ++i) { - if (array->getIndexed(i) == value.rawValue()) + if (array->getIndexed(i) == value.rawValue() && !specialRefs.contains(i)) return i; } } -- cgit v1.2.3 From b7521acd2c77f9f7ace8d49cf1e11affe2ccbd21 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 18 Aug 2015 11:14:56 +0200 Subject: V4 debugger: Fix expression evaluation We need to collect the refs in the debugService's list in order for them to show up on addRefs() and we need to generate proper error responses if either the debugger is not stopped or the evaluation throws an exception. Task-number: QTBUG-47797 Task-number: QTBUG-47816 Change-Id: I98f17c1f3976859ee50b9bfac41091276ff60982 Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index a44acdd370..01d2a98a74 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -317,9 +317,16 @@ ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, void ExpressionEvalJob::handleResult(QV4::ScopedValue &result) { + if (hasExeption()) + exception = result->toQStringNoThrow(); collector->collect(result); } +const QString &ExpressionEvalJob::exceptionMessage() const +{ + return exception; +} + GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine, int seq) : engine(engine) , seq(seq) -- cgit v1.2.3 From 8fe2e6795d9030a7c6f660a0e57b0d85fc36a1f1 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 14 Aug 2015 18:10:38 +0200 Subject: Clean up data format for V4 debug connection This changes the type announced for functions to the actual JavaScript type "function". The type for null is also wrong: it should be "object". However, older QtCreators cannot distinguish between null and {} if null gets the correct type, unless you explicitly compare x === null in an expression evaluator. For this reason the fake "null" type is kept for now. Also, the value field of undefined is now set as QJsonValue::Undefined which causes it to be omitted when sent over the wire. This is the logical thing to do. In addition we add type and value fields for all data members mentioned in a response, not only the ones specifically asked for. The value field is the actual value for any primitives (including strings), or the number of properties for composite types: objects, arrays, functions. In turn, the "ref" members are omitted for primitive types, so that we don't have to hold references to them in the debug service anymore. Even old QtCreators can deal with verbatim data members without "ref". Task-number: QTBUG-47746 Task-number: QTBUG-47747 Change-Id: I773e6418c39cd9814aadb5fb5ef7e109f9a4e618 Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qv4datacollector.cpp | 107 ++++++++++++--------- 1 file changed, 64 insertions(+), 43 deletions(-) (limited to 'src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp') diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index 01d2a98a74..2ef7713ac7 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -109,51 +110,78 @@ void QV4DataCollector::collect(const QV4::ScopedValue &value) m_collectedRefs->append(addRef(value)); } -QJsonObject QV4DataCollector::lookupRef(Ref ref) +const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::ExecutionEngine *engine, + QJsonObject &dict) { - QJsonObject dict; - if (lookupSpecialRef(ref, &dict)) - return dict; - - dict.insert(QStringLiteral("handle"), qint64(ref)); + QV4::Scope scope(engine); + QV4::ScopedValue typeString(scope, QV4::Runtime::typeofValue(engine, value)); + dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow()); - QV4::Scope scope(engine()); - QV4::ScopedValue value(scope, getValue(ref)); + const QLatin1String valueKey("value"); switch (value->type()) { case QV4::Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); - break; + return 0; case QV4::Value::Undefined_Type: - dict.insert(QStringLiteral("type"), QStringLiteral("undefined")); - break; + dict.insert(valueKey, QJsonValue::Undefined); + return 0; case QV4::Value::Null_Type: + // "null" is not the correct type, but we leave this in until QtC can deal with "object" dict.insert(QStringLiteral("type"), QStringLiteral("null")); - break; + dict.insert(valueKey, QJsonValue::Null); + return 0; case QV4::Value::Boolean_Type: - dict.insert(QStringLiteral("type"), QStringLiteral("boolean")); - dict.insert(QStringLiteral("value"), value->booleanValue() ? QStringLiteral("true") - : QStringLiteral("false")); - break; + dict.insert(valueKey, value->booleanValue()); + return 0; case QV4::Value::Managed_Type: - if (QV4::String *s = value->as()) { - dict.insert(QStringLiteral("type"), QStringLiteral("string")); - dict.insert(QStringLiteral("value"), s->toQString()); - } else if (QV4::Object *o = value->as()) { - dict.insert(QStringLiteral("type"), QStringLiteral("object")); - dict.insert(QStringLiteral("properties"), collectProperties(o)); + if (const QV4::String *s = value->as()) { + dict.insert(valueKey, s->toQString()); + } else if (const QV4::ArrayObject *a = value->as()) { + // size of an array is number of its numerical properties; We don't consider free form + // object properties here. + dict.insert(valueKey, qint64(a->getLength())); + return a; + } else if (const QV4::Object *o = value->as()) { + int numProperties = 0; + QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); + QV4::PropertyAttributes attrs; + uint index; + QV4::ScopedProperty p(scope); + QV4::ScopedString name(scope); + while (true) { + it.next(name.getRef(), &index, p, &attrs); + if (attrs.isEmpty()) + break; + else + ++numProperties; + } + dict.insert(valueKey, numProperties); + return o; } else { Q_UNREACHABLE(); } - break; + return 0; case QV4::Value::Integer_Type: - dict.insert(QStringLiteral("type"), QStringLiteral("number")); - dict.insert(QStringLiteral("value"), value->integerValue()); - break; + dict.insert(valueKey, value->integerValue()); + return 0; default: // double - dict.insert(QStringLiteral("type"), QStringLiteral("number")); - dict.insert(QStringLiteral("value"), value->doubleValue()); - break; + dict.insert(valueKey, value->doubleValue()); + return 0; } +} + +QJsonObject QV4DataCollector::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)); + + if (const QV4::Object *o = collectProperty(value, engine(), dict)) + dict.insert(QStringLiteral("properties"), collectProperties(o)); return dict; } @@ -165,7 +193,6 @@ QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionNa 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); @@ -264,7 +291,7 @@ bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict) return true; } -QJsonArray QV4DataCollector::collectProperties(QV4::Object *object) +QJsonArray QV4DataCollector::collectProperties(const QV4::Object *object) { QJsonArray res; @@ -290,20 +317,14 @@ QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::Scop 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()); - dict.insert(QStringLiteral("propertycount"), qint64(obj->getLength())); - } + if (value->isManaged() && !value->isString()) { + Ref ref = addRef(value); + dict.insert(QStringLiteral("ref"), qint64(ref)); + if (m_collectedRefs) + m_collectedRefs->append(ref); } + collectProperty(value, engine(), dict); return dict; } -- cgit v1.2.3