1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "debuggerprotocol.h"
27 
28 #include <QCoreApplication>
29 #include <QDateTime>
30 #include <QDebug>
31 #include <QHostAddress>
32 #include <QTimeZone>
33 #include <QJsonArray>
34 #include <QJsonDocument>
35 
36 #include <ctype.h>
37 
38 #include <utils/processhandle.h>
39 
40 #define QTC_ASSERT_STRINGIFY_HELPER(x) #x
41 #define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
42 #define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
43 #define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
44 #define QTC_CHECK(cond) if (cond) {} else { QTC_ASSERT_STRING(#cond); } do {} while (0)
45 
46 namespace Debugger {
47 namespace Internal {
48 
fromhex(uchar c)49 static uchar fromhex(uchar c)
50 {
51     if (c >= '0' && c <= '9')
52         return c - '0';
53     if (c >= 'a' && c <= 'z')
54         return 10 + c - 'a';
55     if (c >= 'A' && c <= 'Z')
56         return 10 + c - 'A';
57     return UCHAR_MAX;
58 }
59 
60 // DebuggerOutputParser
61 
DebuggerOutputParser(const QString & output)62 DebuggerOutputParser::DebuggerOutputParser(const QString &output)
63     : from(output.begin()), to(output.end())
64 {
65 }
66 
skipCommas()67 void DebuggerOutputParser::skipCommas()
68 {
69     while (from < to && *from == ',')
70         ++from;
71 }
72 
skipSpaces()73 void DebuggerOutputParser::skipSpaces()
74 {
75     while (from < to && isspace(from->unicode()))
76         ++from;
77 }
78 
readString(const std::function<bool (char)> & isValidChar)79 QString DebuggerOutputParser::readString(const std::function<bool(char)> &isValidChar)
80 {
81     QString res;
82     while (from < to && isValidChar(from->unicode()))
83         res += *from++;
84     return res;
85 }
86 
readInt()87 int DebuggerOutputParser::readInt()
88 {
89     int res = 0;
90     while (from < to && *from >= '0' && *from <= '9') {
91         res *= 10;
92         res += (*from++).unicode() - '0';
93     }
94     return res;
95 }
96 
readChar()97 QChar DebuggerOutputParser::readChar()
98 {
99     return *from++;
100 }
101 
isNameChar(char c)102 static bool isNameChar(char c)
103 {
104     return c != '=' && c != ':' && c != ']' && !isspace(c);
105 }
106 
parseResultOrValue(DebuggerOutputParser & parser)107 void GdbMi::parseResultOrValue(DebuggerOutputParser &parser)
108 {
109     parser.skipSpaces();
110 
111     if (parser.isAtEnd())
112         return;
113 
114     //qDebug() << "parseResultOrValue: " << parser.buffer();
115     parseValue(parser);
116     parser.skipSpaces();
117     if (isValid()) {
118         //qDebug() << "no valid result in " << parser.buffer();
119         return;
120     }
121     if (parser.isAtEnd())
122         return;
123     if (parser.isCurrent('(')) {
124         parser.advance();
125         return;
126     }
127 
128     m_name = parser.readString(isNameChar);
129 
130     if (!parser.isAtEnd() && parser.isCurrent('=')) {
131         parser.advance();
132         parseValue(parser);
133     }
134 }
135 
136 // Reads one \ooo entity.
parseOctalEscapedHelper(DebuggerOutputParser & parser,QByteArray & buffer)137 static bool parseOctalEscapedHelper(DebuggerOutputParser &parser, QByteArray &buffer)
138 {
139     if (parser.remainingChars() < 4)
140         return false;
141     if (!parser.isCurrent('\\'))
142         return false;
143 
144     const char c1 = parser.lookAhead(1).unicode();
145     const char c2 = parser.lookAhead(2).unicode();
146     const char c3 = parser.lookAhead(3).unicode();
147     if (!isdigit(c1) || !isdigit(c2) || !isdigit(c3))
148         return false;
149 
150     buffer += char((c1 - '0') * 64 + (c2 - '0') * 8 + (c3 - '0'));
151     parser.advance(4);
152     return true;
153 }
154 
parseHexEscapedHelper(DebuggerOutputParser & parser,QByteArray & buffer)155 static bool parseHexEscapedHelper(DebuggerOutputParser &parser, QByteArray &buffer)
156 {
157     if (parser.remainingChars() < 4)
158         return false;
159     if (!parser.isCurrent('\\'))
160         return false;
161     if (parser.lookAhead(1) != 'x')
162         return false;
163 
164     const char c1 = parser.lookAhead(2).unicode();
165     const char c2 = parser.lookAhead(3).unicode();
166     if (!isxdigit(c1) || !isxdigit(c2))
167         return false;
168 
169     buffer += char(16 * fromhex(c1) + fromhex(c2));
170     parser.advance(4);
171     return true;
172 }
173 
parseSimpleEscape(DebuggerOutputParser & parser,QString & result)174 static void parseSimpleEscape(DebuggerOutputParser &parser, QString &result)
175 {
176     if (parser.isAtEnd()) {
177         qDebug() << "MI Parse Error, unterminated backslash escape";
178         return;
179     }
180 
181     const QChar c = parser.current();
182     parser.advance();
183     switch (c.unicode()) {
184     case 'a': result += '\a'; break;
185     case 'b': result += '\b'; break;
186     case 'f': result += '\f'; break;
187     case 'n': result += '\n'; break;
188     case 'r': result += '\r'; break;
189     case 't': result += '\t'; break;
190     case 'v': result += '\v'; break;
191     case '"': result += '"'; break;
192     case '\'': result += '\''; break;
193     case '\\': result += '\\'; break;
194     default:
195         qDebug() << "MI Parse Error, unrecognized backslash escape";
196     }
197 }
198 
199 // Reads subsequent \123 or \x12 entities and converts to Utf8,
200 // *or* one escaped char, *or* one unescaped char.
parseCharOrEscape(DebuggerOutputParser & parser,QString & result)201 static void parseCharOrEscape(DebuggerOutputParser &parser, QString &result)
202 {
203     QByteArray buffer;
204     while (parseOctalEscapedHelper(parser, buffer))
205         ;
206     while (parseHexEscapedHelper(parser, buffer))
207         ;
208 
209     if (!buffer.isEmpty()) {
210         result.append(QString::fromUtf8(buffer));
211     } else if (parser.isCurrent('\\')) {
212         parser.advance();
213         parseSimpleEscape(parser, result);
214     } else {
215         result += parser.readChar();
216     }
217 }
218 
readCString()219 QString DebuggerOutputParser::readCString()
220 {
221     if (isAtEnd())
222         return QString();
223 
224     if (*from != '"') {
225         qDebug() << "MI Parse Error, double quote expected";
226         ++from; // So we don't hang
227         return QString();
228     }
229 
230     ++from; // Skip initial quote.
231     QString result;
232     result.reserve(to - from);
233     while (from < to) {
234         if (*from == '"') {
235             ++from;
236             return result;
237         }
238         parseCharOrEscape(*this, result);
239     }
240 
241     qDebug() << "MI Parse Error, unfinished string";
242     return QString();
243 }
244 
parseValue(DebuggerOutputParser & parser)245 void GdbMi::parseValue(DebuggerOutputParser &parser)
246 {
247     if (parser.isAtEnd())
248         return;
249 
250     //qDebug() << "parseValue: " << parser;
251     switch (parser.current().unicode()) {
252         case '{':
253             parseTuple(parser);
254             break;
255         case '[':
256             parseList(parser);
257             break;
258         case '"':
259             m_type = Const;
260             m_data = parser.readCString();
261             break;
262         default:
263             break;
264     }
265 }
266 
parseTuple(DebuggerOutputParser & parser)267 void GdbMi::parseTuple(DebuggerOutputParser &parser)
268 {
269     //qDebug() << "parseTuple: " << parser.buffer();
270     QTC_CHECK(parser.isCurrent('{'));
271     parser.advance();
272     parseTuple_helper(parser);
273 }
274 
parseTuple_helper(DebuggerOutputParser & parser)275 void GdbMi::parseTuple_helper(DebuggerOutputParser &parser)
276 {
277     parser.skipCommas();
278     //qDebug() << "parseTuple_helper: " << parser.buffer();
279     QString buf = parser.buffer();
280     m_type = Tuple;
281     while (!parser.isAtEnd()) {
282         if (parser.isCurrent('}')) {
283             parser.advance();
284             break;
285         }
286         GdbMi child;
287         child.parseResultOrValue(parser);
288         //qDebug() << "\n=======\n" << qPrintable(child.toString()) << "\n========\n";
289         if (!child.isValid())
290             return;
291         m_children.push_back(child);
292         parser.skipCommas();
293     }
294 }
295 
parseList(DebuggerOutputParser & parser)296 void GdbMi::parseList(DebuggerOutputParser &parser)
297 {
298     //qDebug() << "parseList: " << parser.buffer();
299     QTC_CHECK(parser.isCurrent('['));
300     parser.advance();
301     m_type = List;
302     parser.skipCommas();
303     while (true) {
304         QTC_ASSERT(!parser.isAtEnd(), break);
305         if (parser.isCurrent(']')) {
306             parser.advance();
307             break;
308         }
309         GdbMi child;
310         child.parseResultOrValue(parser);
311         if (!child.isValid())
312             break;
313         m_children.push_back(child);
314         parser.skipCommas();
315     }
316 }
317 
ind(int indent)318 static QString ind(int indent)
319 {
320     return QString(2 * indent, QChar(' '));
321 }
322 
dumpChildren(QString * str,bool multiline,int indent) const323 void GdbMi::dumpChildren(QString * str, bool multiline, int indent) const
324 {
325     bool first = true;
326     for (const GdbMi &child : *this) {
327         if (first) {
328             first = false;
329         } else {
330             *str += ',';
331             if (multiline)
332                 *str += '\n';
333         }
334 
335         if (multiline)
336             *str += ind(indent);
337         *str += child.toString(multiline, indent);
338     }
339 }
340 
escapeCString(const QString & ba)341 QString GdbMi::escapeCString(const QString &ba)
342 {
343     QString ret;
344     ret.reserve(ba.length() * 2);
345     for (int i = 0; i < ba.length(); ++i) {
346         const ushort c = ba.at(i).unicode();
347         switch (c) {
348             case '\\': ret += "\\\\"; break;
349             case '\a': ret += "\\a"; break;
350             case '\b': ret += "\\b"; break;
351             case '\f': ret += "\\f"; break;
352             case '\n': ret += "\\n"; break;
353             case '\r': ret += "\\r"; break;
354             case '\t': ret += "\\t"; break;
355             case '\v': ret += "\\v"; break;
356             case '"': ret += "\\\""; break;
357             default:
358                 if (c < 32 || c == 127) {
359                     ret += '\\';
360                     ret += QLatin1Char('0' + (c >> 6));
361                     ret += QLatin1Char('0' + ((c >> 3) & 7));
362                     ret += QLatin1Char('0' + (c & 7));
363                 } else {
364                     ret += c;
365                 }
366         }
367     }
368     return ret;
369 }
370 
toString(bool multiline,int indent) const371 QString GdbMi::toString(bool multiline, int indent) const
372 {
373     QString result;
374     switch (m_type) {
375         case Invalid:
376             if (multiline)
377                 result += ind(indent) + "Invalid\n";
378             else
379                 result += "Invalid";
380             break;
381         case Const:
382             if (!m_name.isEmpty())
383                 result += m_name + '=';
384             result += '"' + escapeCString(m_data) + '"';
385             break;
386         case Tuple:
387             if (!m_name.isEmpty())
388                 result += m_name + '=';
389             if (multiline) {
390                 result += "{\n";
391                 dumpChildren(&result, multiline, indent + 1);
392                 result += '\n' + ind(indent) + '}';
393             } else {
394                 result += '{';
395                 dumpChildren(&result, multiline, indent + 1);
396                 result += '}';
397             }
398             break;
399         case List:
400             if (!m_name.isEmpty())
401                 result += m_name + '=';
402             if (multiline) {
403                 result += "[\n";
404                 dumpChildren(&result, multiline, indent + 1);
405                 result += '\n' + ind(indent) + ']';
406             } else {
407                 result += '[';
408                 dumpChildren(&result, multiline, indent + 1);
409                 result += ']';
410             }
411             break;
412     }
413     return result;
414 }
415 
fromString(const QString & ba)416 void GdbMi::fromString(const QString &ba)
417 {
418     DebuggerOutputParser parser(ba);
419     parseResultOrValue(parser);
420 }
421 
fromStringMultiple(const QString & ba)422 void GdbMi::fromStringMultiple(const QString &ba)
423 {
424     DebuggerOutputParser parser(ba);
425     parseTuple_helper(parser);
426 }
427 
operator [](const char * name) const428 const GdbMi &GdbMi::operator[](const char *name) const
429 {
430     static GdbMi empty;
431     for (const GdbMi &child : *this)
432         if (child.m_name == QLatin1String(name))
433             return child;
434     return empty;
435 }
436 
toAddress() const437 qulonglong GdbMi::toAddress() const
438 {
439     QString ba = m_data;
440     if (ba.endsWith('L'))
441         ba.chop(1);
442     if (ba.startsWith('*') || ba.startsWith('@'))
443         ba = ba.mid(1);
444     return ba.toULongLong(nullptr, 0);
445 }
446 
toProcessHandle() const447 Utils::ProcessHandle GdbMi::toProcessHandle() const
448 {
449     return Utils::ProcessHandle(m_data.toULongLong());
450 }
451 
452 //////////////////////////////////////////////////////////////////////////////////
453 //
454 // GdbResponse
455 //
456 //////////////////////////////////////////////////////////////////////////////////
457 
stringFromResultClass(ResultClass resultClass)458 QString DebuggerResponse::stringFromResultClass(ResultClass resultClass)
459 {
460     switch (resultClass) {
461         case ResultDone: return QLatin1String("done");
462         case ResultRunning: return QLatin1String("running");
463         case ResultConnected: return QLatin1String("connected");
464         case ResultError: return QLatin1String("error");
465         case ResultExit: return QLatin1String("exit");
466         default: return QLatin1String("unknown");
467     }
468 }
469 
toString() const470 QString DebuggerResponse::toString() const
471 {
472     QString result;
473     if (token != -1)
474         result = QString::number(token);
475     result += '^';
476     result += stringFromResultClass(resultClass);
477     if (data.isValid())
478         result += ',' + data.toString();
479     result += '\n';
480     return result;
481 }
482 
483 
484 //////////////////////////////////////////////////////////////////////////////////
485 //
486 // GdbResponse
487 //
488 //////////////////////////////////////////////////////////////////////////////////
489 
490 // Tested in tests/auto/debugger/tst_gdb.cpp
491 
492 //! Extract the GDB version number from the output of 'gdb --version'.
493 //! \param[out] gdbVersion GDB version "hash" with major*10000 + minor*100 + patch
494 //!             e.g. version GDB 3.7.14 will set this to 30714
495 //! \param[out] gdbBuildVersion distribution dependent value
496 //! \note See the file tests/auto/debugger/tst_gdb.cpp for example conversions.
extractGdbVersion(const QString & msg,int * gdbVersion,int * gdbBuildVersion,bool * isMacGdb,bool * isQnxGdb)497 void extractGdbVersion(const QString &msg,
498     int *gdbVersion, int *gdbBuildVersion, bool *isMacGdb, bool *isQnxGdb)
499 {
500     const QChar dot('.');
501 
502     const bool ignoreParenthesisContent = msg.contains("rubenvb")
503                                        || msg.contains("openSUSE")
504                                        || msg.contains("SUSE Linux Enterprise");
505 
506     const QChar parOpen('(');
507     const QChar parClose(')');
508 
509     QString cleaned;
510     QString build;
511     bool inClean = true;
512     bool inParenthesis = false;
513 
514     int gdbMsgBegin = msg.indexOf("GNU gdb");
515     if (gdbMsgBegin == -1)
516       gdbMsgBegin = 0;
517 
518     for (int i = gdbMsgBegin, gdbMsgSize = msg.size(); i < gdbMsgSize; ++i) {
519         QChar c = msg.at(i);
520         if (inClean && !cleaned.isEmpty() && c != dot && (c.isPunct() || c.isSpace()))
521             inClean = false;
522         if (ignoreParenthesisContent) {
523             if (!inParenthesis && c == parOpen)
524                 inParenthesis = true;
525             if (inParenthesis && c == parClose)
526                 inParenthesis = false;
527             if (inParenthesis)
528                 continue;
529         }
530         if (inClean) {
531             if (c.isDigit())
532                 cleaned.append(c);
533             else if (!cleaned.isEmpty() && !cleaned.endsWith(dot))
534                 cleaned.append(dot);
535         } else {
536             if (c.isDigit())
537                 build.append(c);
538             else if (!build.isEmpty() && !build.endsWith(dot))
539                 build.append(dot);
540         }
541     }
542 
543     *isMacGdb = msg.contains("Apple version");
544     *isQnxGdb = msg.contains("qnx");
545 
546     *gdbVersion = 10000 * cleaned.section(dot, 0, 0).toInt()
547                   + 100 * cleaned.section(dot, 1, 1).toInt()
548                     + 1 * cleaned.section(dot, 2, 2).toInt();
549     if (cleaned.count(dot) >= 3)
550         *gdbBuildVersion = cleaned.section(dot, 3, 3).toInt();
551     else
552         *gdbBuildVersion = build.section(dot, 0, 0).toInt();
553 
554     if (*isMacGdb)
555         *gdbBuildVersion = build.section(dot, 1, 1).toInt();
556 }
557 
558 //////////////////////////////////////////////////////////////////////////////////
559 //
560 // Decoding
561 //
562 //////////////////////////////////////////////////////////////////////////////////
563 
quoteUnprintableLatin1(const QString & ba)564 static QString quoteUnprintableLatin1(const QString &ba)
565 {
566     QString res;
567     char buf[10];
568     for (int i = 0, n = ba.size(); i != n; ++i) {
569         const unsigned char c = ba.at(i).unicode();
570         if (isprint(c)) {
571             res += ba.at(i);
572         } else {
573             qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
574             res += QLatin1String(buf);
575         }
576     }
577     return res;
578 }
579 
dateFromData(int jd)580 static QDate dateFromData(int jd)
581 {
582     return jd ? QDate::fromJulianDay(jd) : QDate();
583 }
584 
timeFromData(int ms)585 static QTime timeFromData(int ms)
586 {
587     return ms == -1 ? QTime() : QTime(0, 0, 0, 0).addMSecs(ms);
588 }
589 
590 // Stolen and adapted from qdatetime.cpp
getDateTime(qint64 msecs,int status,QDate * date,QTime * time,int tiVersion)591 static void getDateTime(qint64 msecs, int status, QDate *date, QTime *time, int tiVersion)
592 {
593     enum {
594         SECS_PER_DAY = 86400,
595         MSECS_PER_DAY = 86400000,
596         SECS_PER_HOUR = 3600,
597         MSECS_PER_HOUR = 3600000,
598         SECS_PER_MIN = 60,
599         MSECS_PER_MIN = 60000,
600         TIME_T_MAX = 2145916799,  // int maximum 2037-12-31T23:59:59 UTC
601         JULIAN_DAY_FOR_EPOCH = 2440588 // result of julianDayFromDate(1970, 1, 1)
602     };
603 
604     // Status of date/time
605     enum StatusFlag {
606         NullDate            = 0x01,
607         NullTime            = 0x02,
608         ValidDate           = 0x04,
609         ValidTime           = 0x08,
610         ValidDateTime       = 0x10,
611         TimeZoneCached      = 0x20,
612         SetToStandardTime   = 0x40,
613         SetToDaylightTime   = 0x80
614     };
615 
616     qint64 jd = JULIAN_DAY_FOR_EPOCH;
617     qint64 ds = 0;
618 
619     if (qAbs(msecs) >= MSECS_PER_DAY) {
620         jd += (msecs / MSECS_PER_DAY);
621         msecs %= MSECS_PER_DAY;
622     }
623 
624     if (msecs < 0) {
625         ds = MSECS_PER_DAY - msecs - 1;
626         jd -= ds / MSECS_PER_DAY;
627         ds = ds % MSECS_PER_DAY;
628         ds = MSECS_PER_DAY - ds - 1;
629     } else {
630         ds = msecs;
631     }
632 
633     *date = ((status & NullDate) && tiVersion < 14) ? QDate() : QDate::fromJulianDay(jd);
634     *time = ((status & NullTime) && tiVersion < 14) ? QTime() : QTime::fromMSecsSinceStartOfDay(ds);
635 }
636 
decodeData(const QString & ba,const QString & encoding)637 QString decodeData(const QString &ba, const QString &encoding)
638 {
639     if (encoding.isEmpty())
640         return quoteUnprintableLatin1(ba); // The common case.
641 
642     if (encoding == "empty")
643         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<empty>");
644     if (encoding == "minimumitemcount")
645         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<at least %n items>", nullptr, ba.toInt());
646     if (encoding == "undefined")
647         return QLatin1String("Undefined");
648     if (encoding == "null")
649         return QLatin1String("Null");
650     if (encoding == "itemcount")
651         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<%n items>", nullptr, ba.toInt());
652     if (encoding == "notaccessible")
653         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<not accessible>");
654     if (encoding == "optimizedout")
655         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<optimized out>");
656     if (encoding == "nullreference")
657         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<null reference>");
658     if (encoding == "emptystructure")
659         return QLatin1String("{...}");
660     if (encoding == "uninitialized")
661         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<uninitialized>");
662     if (encoding == "invalid")
663         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<invalid>");
664     if (encoding == "notcallable")
665         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<not callable>");
666     if (encoding == "outofscope")
667         return QCoreApplication::translate("Debugger::Internal::WatchHandler", "<out of scope>");
668 
669     DebuggerEncoding enc(encoding);
670     QString result;
671     switch (enc.type) {
672         case DebuggerEncoding::Unencoded: {
673             result = quoteUnprintableLatin1(ba);
674             break;
675         }
676         case DebuggerEncoding::HexEncodedLocal8Bit: {
677             const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
678             result = QString::fromLocal8Bit(decodedBa.data(), decodedBa.size());
679             break;
680         }
681         case DebuggerEncoding::HexEncodedLatin1: {
682             const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
683             result = QString::fromLatin1(decodedBa.data(), decodedBa.size());
684             break;
685         }
686         case DebuggerEncoding::HexEncodedUtf8: {
687             const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
688             result = QString::fromUtf8(decodedBa.data(), decodedBa.size());
689             break;
690         }
691         case DebuggerEncoding::HexEncodedUtf16: {
692             const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
693             result = QString::fromUtf16(reinterpret_cast<const ushort *>
694                 (decodedBa.data()), decodedBa.size() / 2);
695             break;
696         }
697         case DebuggerEncoding::HexEncodedUcs4: {
698             const QByteArray decodedBa = QByteArray::fromHex(ba.toUtf8());
699             result = QString::fromUcs4(reinterpret_cast<const uint *>
700                 (decodedBa.data()), decodedBa.size() / 4);
701             break;
702         }
703         case DebuggerEncoding::JulianDate: {
704             const QDate date = dateFromData(ba.toInt());
705             return date.isValid() ? date.toString(Qt::TextDate) : "(invalid)";
706         }
707         case DebuggerEncoding::MillisecondsSinceMidnight: {
708             const QTime time = timeFromData(ba.toInt());
709             return time.isValid() ? time.toString(Qt::TextDate) : "(invalid)";
710         }
711         case DebuggerEncoding::JulianDateAndMillisecondsSinceMidnight: {
712             const int p = ba.indexOf('/');
713             const QDate date = dateFromData(ba.left(p).toInt());
714             const QTime time = timeFromData(ba.mid(p + 1).toInt());
715             const QDateTime dateTime = QDateTime(date, time);
716             return dateTime.isValid() ? dateTime.toString(Qt::TextDate) : "(invalid)";
717         }
718         case DebuggerEncoding::HexEncodedUnsignedInteger:
719         case DebuggerEncoding::HexEncodedSignedInteger:
720             qDebug("not implemented"); // Only used in Arrays, see watchdata.cpp
721             return QString();
722         case DebuggerEncoding::HexEncodedFloat: {
723             QByteArray s = QByteArray::fromHex(ba.toUtf8());
724             if (s.size() < enc.size)
725                 s.prepend(QByteArray(enc.size - s.size(), '\0'));
726             if (enc.size == 4) {
727                 union { char c[4]; float f; } u = {{s[3], s[2], s[1], s[0]}};
728                 return QString::number(u.f);
729             }
730             if (enc.size == 8) {
731                 union { char c[8]; double d; } u = {{s[7], s[6], s[5], s[4], s[3], s[2], s[1], s[0]}};
732                 return QString::number(u.d);
733             }
734             break;
735         }
736         case DebuggerEncoding::IPv6AddressAndHexScopeId: { // 16 hex-encoded bytes, "%" and the string-encoded scope
737             const int p = ba.indexOf('%');
738             QHostAddress ip6(p == -1 ? ba : ba.left(p));
739             if (ip6.isNull())
740                 break;
741 
742             const QByteArray scopeId = p == -1 ? QByteArray() : QByteArray::fromHex(ba.mid(p + 1).toUtf8());
743             if (!scopeId.isEmpty())
744                 ip6.setScopeId(QString::fromUtf16(reinterpret_cast<const ushort *>(scopeId.constData()),
745                                                   scopeId.length() / 2));
746             return ip6.toString();
747         }
748         case DebuggerEncoding::DateTimeInternal: { // DateTimeInternal: msecs, spec, offset, tz, status
749             int p0 = ba.indexOf('/');
750             int p1 = ba.indexOf('/', p0 + 1);
751             int p2 = ba.indexOf('/', p1 + 1);
752             int p3 = ba.indexOf('/', p2 + 1);
753             int p4 = ba.indexOf('/', p3 + 1);
754 
755             qint64 msecs = ba.left(p0).toLongLong();
756             ++p0;
757             Qt::TimeSpec spec = Qt::TimeSpec(ba.mid(p0, p1 - p0).toInt());
758             ++p1;
759             qulonglong offset = ba.mid(p1, p2 - p1).toInt();
760             ++p2;
761             QByteArray timeZoneId = QByteArray::fromHex(ba.mid(p2, p3 - p2).toUtf8());
762             ++p3;
763             int status = ba.mid(p3, p4 - p3).toInt();
764             ++p4;
765             int tiVersion = ba.mid(p4).toInt();
766 
767             QDate date;
768             QTime time;
769             getDateTime(msecs, status, &date, &time, tiVersion);
770 
771             QDateTime dateTime;
772             if (spec == Qt::OffsetFromUTC) {
773                 dateTime = QDateTime(date, time, spec, offset);
774             } else if (spec == Qt::TimeZone) {
775                 if (!QTimeZone::isTimeZoneIdAvailable(timeZoneId))
776                     return QLatin1String("<unavailable>");
777                 dateTime = QDateTime(date, time, QTimeZone(timeZoneId));
778             } else {
779                 dateTime = QDateTime(date, time, spec);
780             }
781             return dateTime.toString();
782         }
783         qDebug() << "ENCODING ERROR: " << enc.type;
784         return QCoreApplication::translate("Debugger", "<Encoding error>");
785     }
786 
787     if (enc.quotes) {
788         const QChar doubleQuote('"');
789         result = doubleQuote + result + doubleQuote;
790     }
791     return result;
792 }
793 
794 //////////////////////////////////////////////////////////////////////////////////
795 //
796 // DebuggerCommand
797 //
798 //////////////////////////////////////////////////////////////////////////////////
799 
800 template<typename Value>
addToJsonObject(const QJsonValue & args,const char * name,const Value & value)801 QJsonValue addToJsonObject(const QJsonValue &args, const char *name, const Value &value)
802 {
803     QTC_ASSERT(args.isObject() || args.isNull(), return args);
804     QJsonObject obj = args.toObject();
805     obj.insert(QLatin1String(name), value);
806     return obj;
807 }
808 
arg(const char * name,int value)809 void DebuggerCommand::arg(const char *name, int value)
810 {
811     args = addToJsonObject(args, name, value);
812 }
813 
arg(const char * name,qlonglong value)814 void DebuggerCommand::arg(const char *name, qlonglong value)
815 {
816     args = addToJsonObject(args, name, value);
817 }
818 
arg(const char * name,qulonglong value)819 void DebuggerCommand::arg(const char *name, qulonglong value)
820 {
821     // gdb and lldb will correctly cast the value back to unsigned if needed, so this is no problem.
822     args = addToJsonObject(args, name, qint64(value));
823 }
824 
arg(const char * name,const QString & value)825 void DebuggerCommand::arg(const char *name, const QString &value)
826 {
827     args = addToJsonObject(args, name, value);
828 }
829 
arg(const char * name,const char * value)830 void DebuggerCommand::arg(const char *name, const char *value)
831 {
832     args = addToJsonObject(args, name, value);
833 }
834 
arg(const char * name,const QList<int> & list)835 void DebuggerCommand::arg(const char *name, const QList<int> &list)
836 {
837     QJsonArray numbers;
838     for (int item : list)
839         numbers.append(item);
840     args = addToJsonObject(args, name, numbers);
841 }
842 
arg(const char * name,const QStringList & list)843 void DebuggerCommand::arg(const char *name, const QStringList &list)
844 {
845     QJsonArray arr;
846     for (const QString &item : list)
847         arr.append(toHex(item));
848     args = addToJsonObject(args, name, arr);
849 }
850 
arg(const char * value)851 void DebuggerCommand::arg(const char *value)
852 {
853     QTC_ASSERT(args.isArray() || args.isNull(), return);
854     QJsonArray arr = args.toArray();
855     arr.append(value);
856     args = arr;
857 }
858 
arg(const char * name,bool value)859 void DebuggerCommand::arg(const char *name, bool value)
860 {
861     args = addToJsonObject(args, name, value);
862 }
863 
arg(const char * name,const QJsonValue & value)864 void DebuggerCommand::arg(const char *name, const QJsonValue &value)
865 {
866     args = addToJsonObject(args, name, value);
867 }
868 
arg(const char * name,const Utils::FilePath & filePath)869 void DebuggerCommand::arg(const char *name, const Utils::FilePath &filePath)
870 {
871     args = addToJsonObject(args, name, filePath.toString());
872 }
873 
translateJsonToPython(const QJsonValue & value)874 static QJsonValue translateJsonToPython(const QJsonValue &value)
875 {
876     // TODO: Verify that this covers all incompatibilities between python and json,
877     //       e.g. number format and precision
878 
879     switch (value.type()) {
880     // Undefined is not a problem as the JSON generator ignores that.
881     case QJsonValue::Null:
882         // Python doesn't understand "null"
883         return QJsonValue(0);
884     case QJsonValue::Bool:
885         // Python doesn't understand lowercase "true" or "false"
886         return QJsonValue(value.toBool() ? 1 : 0);
887     case QJsonValue::Object: {
888         QJsonObject object = value.toObject();
889         for (QJsonObject::iterator i = object.begin(); i != object.end(); ++i)
890             i.value() = translateJsonToPython(i.value());
891         return object;
892     }
893     case QJsonValue::Array: {
894         QJsonArray array = value.toArray();
895         for (QJsonArray::iterator i = array.begin(); i != array.end(); ++i)
896             *i = translateJsonToPython(*i);
897         return array;
898     }
899     default:
900         return value;
901     }
902 }
903 
argsToPython() const904 QString DebuggerCommand::argsToPython() const
905 {
906     QJsonValue pythonCompatible(translateJsonToPython(args));
907     if (pythonCompatible.isArray())
908         return QString::fromUtf8(QJsonDocument(pythonCompatible.toArray()).toJson(QJsonDocument::Compact));
909     else
910         return QString::fromUtf8(QJsonDocument(pythonCompatible.toObject()).toJson(QJsonDocument::Compact));
911 }
912 
argsToString() const913 QString DebuggerCommand::argsToString() const
914 {
915     return args.toString();
916 }
917 
DebuggerEncoding(const QString & data)918 DebuggerEncoding::DebuggerEncoding(const QString &data)
919 {
920     const QStringList l = data.split(':');
921 
922     const QString &t = l.at(0);
923     if (t == "latin1") {
924         type = HexEncodedLatin1;
925         size = 1;
926         quotes = true;
927     } else if (t == "local8bit") {
928         type = HexEncodedLocal8Bit;
929         size = 1;
930         quotes = true;
931     } else if (t == "utf8") {
932         type = HexEncodedUtf8;
933         size = 1;
934         quotes = true;
935     } else if (t == "utf16") {
936         type = HexEncodedUtf16;
937         size = 2;
938         quotes = true;
939     } else if (t == "ucs4") {
940         type = HexEncodedUcs4;
941         size = 4;
942         quotes = true;
943     } else if (t == "int") {
944         type = HexEncodedSignedInteger;
945     } else if (t == "uint") {
946         type = HexEncodedUnsignedInteger;
947     } else if (t == "float") {
948         type = HexEncodedFloat;
949     } else if (t == "juliandate") {
950         type = JulianDate;
951     } else if (t == "juliandateandmillisecondssincemidnight") {
952         type = JulianDateAndMillisecondsSinceMidnight;
953     } else if (t == "millisecondssincemidnight") {
954         type = MillisecondsSinceMidnight;
955     } else if (t == "ipv6addressandhexscopeid") {
956         type = IPv6AddressAndHexScopeId;
957     } else if (t == "datetimeinternal") {
958         type = DateTimeInternal;
959     } else if (!t.isEmpty()) {
960         qDebug() << "CANNOT DECODE ENCODING" << data;
961     }
962 
963     if (l.size() >= 2)
964         size = l.at(1).toInt();
965 
966     if (l.size() >= 3)
967         quotes = bool(l.at(2).toInt());
968 }
969 
toString() const970 QString DebuggerEncoding::toString() const
971 {
972     return QString("%1:%2:%3").arg(type).arg(size).arg(quotes);
973 }
974 
fromHex(const QString & str)975 QString fromHex(const QString &str)
976 {
977     return QString::fromUtf8(QByteArray::fromHex(str.toUtf8()));
978 }
979 
toHex(const QString & str)980 QString toHex(const QString &str)
981 {
982     return QString::fromUtf8(str.toUtf8().toHex());
983 }
984 
985 } // namespace Internal
986 } // namespace Debugger
987