############################################################################
#
# Copyright (C) 2016 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of Qt Creator.
#
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
############################################################################

import os
import struct
import sys
import base64
import re
import time
import json
import inspect

if sys.version_info[0] >= 3:
    xrange = range
    toInteger = int
else:
    toInteger = long


verbosity = 0
verbosity = 1

# Debugger start modes. Keep in sync with DebuggerStartMode in debuggerconstants.h
NoStartMode, \
StartInternal, \
StartExternal,  \
AttachExternal,  \
AttachCrashedExternal,  \
AttachCore, \
AttachToRemoteServer, \
AttachToRemoteProcess, \
StartRemoteProcess, \
    = range(0, 9)


# Known special formats. Keep in sync with DisplayFormat in debuggerprotocol.h
AutomaticFormat, \
RawFormat, \
SimpleFormat, \
EnhancedFormat, \
SeparateFormat, \
Latin1StringFormat, \
SeparateLatin1StringFormat, \
Utf8StringFormat, \
SeparateUtf8StringFormat, \
Local8BitStringFormat, \
Utf16StringFormat, \
Ucs4StringFormat, \
Array10Format, \
Array100Format, \
Array1000Format, \
Array10000Format, \
ArrayPlotFormat, \
CompactMapFormat, \
DirectQListStorageFormat, \
IndirectQListStorageFormat, \
    = range(0, 20)

# Breakpoints. Keep synchronized with BreakpointType in breakpoint.h
UnknownType, \
BreakpointByFileAndLine, \
BreakpointByFunction, \
BreakpointByAddress, \
BreakpointAtThrow, \
BreakpointAtCatch, \
BreakpointAtMain, \
BreakpointAtFork, \
BreakpointAtExec, \
BreakpointAtSysCall, \
WatchpointAtAddress, \
WatchpointAtExpression, \
BreakpointOnQmlSignalEmit, \
BreakpointAtJavaScriptThrow, \
    = range(0, 14)


def arrayForms():
    return [ArrayPlotFormat]

def mapForms():
    return [CompactMapFormat]


class ReportItem:
    """
    Helper structure to keep temporary "best" information about a value
    or a type scheduled to be reported. This might get overridden be
    subsequent better guesses during a putItem() run.
    """
    def __init__(self, value = None, encoding = None, priority = -100, elided = None):
        self.value = value
        self.priority = priority
        self.encoding = encoding
        self.elided = elided

    def __str__(self):
        return "Item(value: %s, encoding: %s, priority: %s, elided: %s)" \
            % (self.value, self.encoding, self.priority, self.elided)


class Blob(object):
    """
    Helper structure to keep a blob of bytes, possibly
    in the inferior.
    """

    def __init__(self, data, isComplete = True):
        self.data = data
        self.size = len(data)
        self.isComplete = isComplete

    def size(self):
        return self.size

    def toBytes(self):
        """Retrieves "lazy" contents from memoryviews."""
        data = self.data

        major = sys.version_info[0]
        if major == 3 or (major == 2 and sys.version_info[1] >= 7):
            if isinstance(data, memoryview):
                data = data.tobytes()
        if major == 2 and isinstance(data, buffer):
            data = ''.join([c for c in data])
        return data

    def toString(self):
        data = self.toBytes()
        return data if sys.version_info[0] == 2 else data.decode("utf8")

    def extractByte(self, offset = 0):
        return struct.unpack_from("b", self.data, offset)[0]

    def extractShort(self, offset = 0):
        return struct.unpack_from("h", self.data, offset)[0]

    def extractUShort(self, offset = 0):
        return struct.unpack_from("H", self.data, offset)[0]

    def extractInt(self, offset = 0):
        return struct.unpack_from("i", self.data, offset)[0]

    def extractUInt(self, offset = 0):
        return struct.unpack_from("I", self.data, offset)[0]

    def extractLong(self, offset = 0):
        return struct.unpack_from("l", self.data, offset)[0]

    # FIXME: Note these should take target architecture into account.
    def extractULong(self, offset = 0):
        return struct.unpack_from("L", self.data, offset)[0]

    def extractInt64(self, offset = 0):
        return struct.unpack_from("q", self.data, offset)[0]

    def extractUInt64(self, offset = 0):
        return struct.unpack_from("Q", self.data, offset)[0]

    def extractDouble(self, offset = 0):
        return struct.unpack_from("d", self.data, offset)[0]

    def extractFloat(self, offset = 0):
        return struct.unpack_from("f", self.data, offset)[0]

def warn(message):
    print('bridgemessage={msg="%s"},' % message.replace('"', '$').encode("latin1"))


def showException(msg, exType, exValue, exTraceback):
    warn("**** CAUGHT EXCEPTION: %s ****" % msg)
    try:
        import traceback
        for line in traceback.format_exception(exType, exValue, exTraceback):
            warn("%s" % line)
    except:
        pass


class Children:
    def __init__(self, d, numChild = 1, childType = None, childNumChild = None,
            maxNumChild = None, addrBase = None, addrStep = None):
        self.d = d
        self.numChild = numChild
        self.childNumChild = childNumChild
        self.maxNumChild = maxNumChild
        self.addrBase = addrBase
        self.addrStep = addrStep
        self.printsAddress = True
        if childType is None:
            self.childType = None
        else:
            self.childType = d.stripClassTag(str(childType))
            if not self.d.isCli:
                self.d.put('childtype="%s",' % self.childType)
            if childNumChild is None:
                pass
                #if self.d.isSimpleType(childType):
                #    self.d.put('childnumchild="0",')
                #    self.childNumChild = 0
                #elif childType.code == PointerCode:
                #    self.d.put('childnumchild="1",')
                #    self.childNumChild = 1
            else:
                self.d.put('childnumchild="%s",' % childNumChild)
                self.childNumChild = childNumChild
        self.printsAddress = not self.d.putAddressRange(addrBase, addrStep)

    def __enter__(self):
        self.savedChildType = self.d.currentChildType
        self.savedChildNumChild = self.d.currentChildNumChild
        self.savedNumChild = self.d.currentNumChild
        self.savedMaxNumChild = self.d.currentMaxNumChild
        self.savedPrintsAddress = self.d.currentPrintsAddress
        self.d.currentChildType = self.childType
        self.d.currentChildNumChild = self.childNumChild
        self.d.currentNumChild = self.numChild
        self.d.currentMaxNumChild = self.maxNumChild
        self.d.currentPrintsAddress = self.printsAddress
        self.d.put(self.d.childrenPrefix)

    def __exit__(self, exType, exValue, exTraceBack):
        if not exType is None:
            if self.d.passExceptions:
                showException("CHILDREN", exType, exValue, exTraceBack)
            self.d.putNumChild(0)
            self.d.putSpecialValue("notaccessible")
        if not self.d.currentMaxNumChild is None:
            if self.d.currentMaxNumChild < self.d.currentNumChild:
                self.d.put('{name="<incomplete>",value="",type="",numchild="0"},')
        self.d.currentChildType = self.savedChildType
        self.d.currentChildNumChild = self.savedChildNumChild
        self.d.currentNumChild = self.savedNumChild
        self.d.currentMaxNumChild = self.savedMaxNumChild
        self.d.currentPrintsAddress = self.savedPrintsAddress
        self.d.putNewline()
        self.d.put(self.d.childrenSuffix)
        return True

class PairedChildrenData:
    def __init__(self, d, pairType, keyType, valueType, useKeyAndValue):
        self.useKeyAndValue = useKeyAndValue
        self.pairType = pairType
        self.keyType = keyType
        self.valueType = valueType
        self.isCompact = d.isMapCompact(self.keyType, self.valueType)
        self.childType = valueType if self.isCompact else pairType
        ns = d.qtNamespace()
        keyTypeName = d.stripClassTag(str(self.keyType))
        self.keyIsQString = keyTypeName == ns + "QString"
        self.keyIsQByteArray = keyTypeName == ns + "QByteArray"
        self.keyIsStdString = keyTypeName == "std::string" \
            or keyTypeName.startswith("std::basic_string<char")

class PairedChildren(Children):
    def __init__(self, d, numChild, useKeyAndValue = False,
            pairType = None, keyType = None, valueType = None, maxNumChild = None):
        self.d = d
        if keyType is None:
            keyType = d.templateArgument(pairType, 0).unqualified()
        if valueType is None:
            valueType = d.templateArgument(pairType, 1)
        d.pairData = PairedChildrenData(d, pairType, keyType, valueType, useKeyAndValue)

        Children.__init__(self, d, numChild,
            d.pairData.childType,
            maxNumChild = maxNumChild,
            addrBase = None, addrStep = None)

    def __enter__(self):
        self.savedPairData = self.d.pairData if hasattr(self.d, "pairData") else None
        Children.__enter__(self)

    def __exit__(self, exType, exValue, exTraceBack):
        Children.__exit__(self, exType, exValue, exTraceBack)
        self.d.pairData = self.savedPairData if self.savedPairData else None


class SubItem:
    def __init__(self, d, component):
        self.d = d
        self.name = component
        self.iname = None

    def __enter__(self):
        self.d.enterSubItem(self)

    def __exit__(self, exType, exValue, exTraceBack):
        return self.d.exitSubItem(self, exType, exValue, exTraceBack)

class NoAddress:
    def __init__(self, d):
        self.d = d

    def __enter__(self):
        self.savedPrintsAddress = self.d.currentPrintsAddress
        self.d.currentPrintsAddress = False

    def __exit__(self, exType, exValue, exTraceBack):
        self.d.currentPrintsAddress = self.savedPrintsAddress

class TopLevelItem(SubItem):
    def __init__(self, d, iname):
        self.d = d
        self.iname = iname
        self.name = None

class UnnamedSubItem(SubItem):
    def __init__(self, d, component):
        self.d = d
        self.iname = "%s.%s" % (self.d.currentIName, component)
        self.name = None

class DumperBase:
    def __init__(self):
        self.isCdb = False
        self.isGdb = False
        self.isLldb = False
        self.isCli = False

        # Later set, or not set:
        self.stringCutOff = 10000
        self.displayStringLimit = 100

        self.resetCaches()
        self.resetStats()

        self.childrenPrefix = 'children=['
        self.childrenSuffix = '],'

        self.dumpermodules = [
            "qttypes",
            "stdtypes",
            "misctypes",
            "boosttypes",
            "creatortypes",
            "personaltypes",
        ]


    def resetCaches(self):
        # This is a cache mapping from 'type name' to 'display alternatives'.
        self.qqFormats = { "QVariant (QVariantMap)" : mapForms() }

        # This is a cache of all known dumpers.
        self.qqDumpers = {}    # Direct type match
        self.qqDumpersEx = {}  # Using regexp

        # This is a cache of all dumpers that support writing.
        self.qqEditable = {}

        # This keeps canonical forms of the typenames, without array indices etc.
        self.cachedFormats = {}

        # Maps type names to static metaobjects. If a type is known
        # to not be QObject derived, it contains a 0 value.
        self.knownStaticMetaObjects = {}

        self.counts = {}
        self.pretimings = {}
        self.timings = []

    def resetStats(self):
        # Timing collection
        self.pretimings = {}
        self.timings = []
        pass

    def dumpStats(self):
        msg = [self.counts, self.timings]
        self.resetStats()
        return msg

    def bump(self, key):
        if key in self.counts:
            self.counts[key] += 1
        else:
            self.counts[key] = 1

    def preping(self, key):
        import time
        self.pretimings[key] = time.time()

    def ping(self, key):
        import time
        elapsed = int(1000000 * (time.time() - self.pretimings[key]))
        self.timings.append([key, elapsed])

    def putNewline(self):
        pass

    def stripClassTag(self, typeName):
        if typeName.startswith("class "):
            return typeName[6:]
        if typeName.startswith("struct "):
            return typeName[7:]
        if typeName.startswith("const "):
            return typeName[6:]
        if typeName.startswith("volatile "):
            return typeName[9:]
        return typeName

    def stripForFormat(self, typeName):
        if typeName in self.cachedFormats:
            return self.cachedFormats[typeName]
        stripped = ""
        inArray = 0
        for c in self.stripClassTag(typeName):
            if c == '<':
                break
            if c == ' ':
                continue
            if c == '[':
                inArray += 1
            elif c == ']':
                inArray -= 1
            if inArray and ord(c) >= 48 and ord(c) <= 57:
                continue
            stripped +=  c
        self.cachedFormats[typeName] = stripped
        return stripped

    # Hex decoding operating on str, return str.
    def hexdecode(self, s):
        if sys.version_info[0] == 2:
            return s.decode("hex")
        return bytes.fromhex(s).decode("utf8")

    # Hex encoding operating on str or bytes, return str.
    def hexencode(self, s):
        if s is None:
            s = ''
        if sys.version_info[0] == 2:
            return s.encode("hex")
        if isinstance(s, str):
            s = s.encode("utf8")
        return base64.b16encode(s).decode("utf8")

    #def toBlob(self, value):
    #    """Abstract"""

    def is32bit(self):
        return self.ptrSize() == 4

    def is64bit(self):
        return self.ptrSize() == 8

    def isQt3Support(self):
        # assume no Qt 3 support by default
        return False

    def lookupQtType(self, typeName):
        return self.lookupType(self.qtNamespace() + typeName)

    # Clamps size to limit.
    def computeLimit(self, size, limit):
        if limit == 0:
            limit = self.displayStringLimit
        if limit is None or size <= limit:
            return 0, size
        return size, limit

    def vectorDataHelper(self, addr):
        if self.qtVersion() >= 0x050000:
            size = self.extractInt(addr + 4)
            alloc = self.extractInt(addr + 8) & 0x7ffffff
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
        else:
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
            data = addr + 16
        return data, size, alloc

    def byteArrayDataHelper(self, addr):
        if self.qtVersion() >= 0x050000:
            # QTypedArray:
            # - QtPrivate::RefCount ref
            # - int size
            # - uint alloc : 31, capacityReserved : 1
            # - qptrdiff offset
            size = self.extractInt(addr + 4)
            alloc = self.extractInt(addr + 8) & 0x7ffffff
            data = addr + self.extractPointer(addr + 8 + self.ptrSize())
            if self.ptrSize() == 4:
                data = data & 0xffffffff
            else:
                data = data & 0xffffffffffffffff
        elif self.qtVersion() >= 0x040000:
            # Data:
            # - QBasicAtomicInt ref;
            # - int alloc, size;
            # - [padding]
            # - char *data;
            alloc = self.extractInt(addr + 4)
            size = self.extractInt(addr + 8)
            data = self.extractPointer(addr + 8 + self.ptrSize())
        else:
            # Data:
            # - QShared count;
            # - QChar *unicode
            # - char *ascii
            # - uint len: 30
            size = self.extractInt(addr + 3 * self.ptrSize()) & 0x3ffffff
            alloc = size  # pretend.
            data = self.extractPointer(addr + self.ptrSize())
        return data, size, alloc

    # addr is the begin of a QByteArrayData structure
    def encodeStringHelper(self, addr, limit):
        # Should not happen, but we get it with LLDB as result
        # of inferior calls
        if addr == 0:
            return 0, ""
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, 2 * shown)

    def encodeByteArrayHelper(self, addr, limit):
        data, size, alloc = self.byteArrayDataHelper(addr)
        if alloc != 0:
            self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
        elided, shown = self.computeLimit(size, limit)
        return elided, self.readMemory(data, shown)

    def putCharArrayHelper(self, data, size, charSize,
                           displayFormat = AutomaticFormat,
                           makeExpandable = True):
        bytelen = size * charSize
        elided, shown = self.computeLimit(bytelen, self.displayStringLimit)
        mem = self.readMemory(data, shown)
        if charSize == 1:
            if displayFormat == Latin1StringFormat \
                    or displayFormat == SeparateLatin1StringFormat:
                encodingType = "latin1"
            else:
                encodingType = "utf8"
            childType = "char"
        elif charSize == 2:
            encodingType = "utf16"
            childType = "short"
        else:
            encodingType = "ucs4"
            childType = "int"

        self.putValue(mem, encodingType, elided=elided)

        if displayFormat == SeparateLatin1StringFormat \
                or displayFormat == SeparateUtf8StringFormat \
                or displayFormat == SeparateFormat:
            elided, shown = self.computeLimit(bytelen, 100000)
            self.putDisplay(encodingType + ':separate', self.readMemory(data, shown))

        if makeExpandable:
            self.putNumChild(size)
            if self.isExpanded():
                with Children(self):
                    for i in range(size):
                        self.putSubItem(size, data[i])

    def readMemory(self, addr, size):
        data = self.extractBlob(addr, size).toBytes()
        return self.hexencode(data)

    def encodeByteArray(self, value, limit = 0):
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), limit)
        return data

    def byteArrayData(self, value):
        return self.byteArrayDataHelper(self.extractPointer(value))

    def putByteArrayValue(self, value):
        elided, data = self.encodeByteArrayHelper(self.extractPointer(value), self.displayStringLimit)
        self.putValue(data, "latin1", elided=elided)

    def encodeString(self, value, limit = 0):
        elided, data = self.encodeStringHelper(self.extractPointer(value), limit)
        return data

    def encodedUtf16ToUtf8(self, s):
        return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 4)])

    def encodeStringUtf8(self, value, limit = 0):
        return self.encodedUtf16ToUtf8(self.encodeString(value, limit))

    def stringData(self, value):
        return self.byteArrayDataHelper(self.extractPointer(value))

    def encodeStdString(self, value, limit = 0):
        data = value["_M_dataplus"]["_M_p"]
        sizePtr = data.cast(self.sizetType().pointer())
        size = int(sizePtr[-3])
        alloc = int(sizePtr[-2])
        self.check(0 <= size and size <= alloc and alloc <= 100*1000*1000)
        elided, shown = self.computeLimit(size, limit)
        return self.readMemory(data, shown)

    def extractTemplateArgument(self, typename, position):
        level = 0
        skipSpace = False
        inner = ''
        for c in typename[typename.find('<') + 1 : -1]:
            if c == '<':
                inner += c
                level += 1
            elif c == '>':
                level -= 1
                inner += c
            elif c == ',':
                if level == 0:
                    if position == 0:
                        return inner.strip()
                    position -= 1
                    inner = ''
                else:
                    inner += c
                    skipSpace = True
            else:
                if skipSpace and c == ' ':
                    pass
                else:
                    inner += c
                    skipSpace = False
        # Handle local struct definitions like QList<main(int, char**)::SomeStruct>
        inner = inner.strip()
        p = inner.find(')::')
        if p > -1:
            inner = inner[p+3:]
        return inner

    def putStringValueByAddress(self, addr):
        elided, data = self.encodeStringHelper(addr, self.displayStringLimit)
        self.putValue(data, "utf16", elided=elided)

    def putStringValue(self, value):
        elided, data = self.encodeStringHelper(
            self.extractPointer(value),
            self.displayStringLimit)
        self.putValue(data, "utf16", elided=elided)

    def putAddressItem(self, name, value, type = ""):
        with SubItem(self, name):
            self.putValue("0x%x" % value)
            self.putType(type)
            self.putNumChild(0)

    def putIntItem(self, name, value):
        with SubItem(self, name):
            self.putValue(value)
            self.putType("int")
            self.putNumChild(0)

    def putBoolItem(self, name, value):
        with SubItem(self, name):
            self.putValue(value)
            self.putType("bool")
            self.putNumChild(0)

    def putGenericItem(self, name, type, value, encoding = None):
        with SubItem(self, name):
            self.putValue(value, encoding)
            self.putType(type)
            self.putNumChild(0)

    def putCallItem(self, name, value, func, *args):
        try:
            result = self.callHelper(value, func, args)
            with SubItem(self, name):
                self.putItem(result)
        except:
            with SubItem(self, name):
                self.putSpecialValue("notcallable");
                self.putNumChild(0)

    def call(self, value, func, *args):
        return self.callHelper(value, func, args)

    def putAddressRange(self, base, step):
        try:
            if not addrBase is None and not step is None:
                self.put('addrbase="0x%x",' % toInteger(base))
                self.put('addrstep="0x%x",' % toInteger(step))
                return True
        except:
            #warn("ADDRBASE: %s" % base)
            #warn("ADDRSTEP: %s" % step)
            pass
        return False

    def putMapName(self, value, index = None):
        ns = self.qtNamespace()
        typeName = self.stripClassTag(str(value.type))
        if typeName == ns + "QString":
            self.put('keyencoded="utf16:2:0",key="%s",' % self.encodeString(value))
        elif typeName == ns + "QByteArray":
            self.put('keyencoded="latin1:1:0",key="%s",' % self.encodeByteArray(value))
        elif typeName == "std::string":
            self.put('keyencoded="latin1:1:0",key="%s",' % self.encodeStdString(value))
        else:
            val = str(value.GetValue()) if self.isLldb else str(value)
            if index is None:
                key = '%s' % val
            else:
                key = '[%s] %s' % (index, val)
            self.put('keyencoded="utf8:1:0",key="%s",' % self.hexencode(key))

    def putPair(self, pair, index = None):
        if self.pairData.useKeyAndValue:
            key = pair["key"]
            value = pair["value"]
        else:
            key = pair["first"]
            value = pair["second"]
        if self.pairData.isCompact:
            if self.pairData.keyIsQString:
                self.put('keyencoded="utf16",key="%s",' % self.encodeString(key))
            elif self.pairData.keyIsQByteArray:
                self.put('keyencoded="latin1",key="%s",' % self.encodeByteArray(key))
            elif self.pairData.keyIsStdString:
                self.put('keyencoded="latin1",key="%s",' % self.encodeStdString(key))
            else:
                name = str(key.GetValue()) if self.isLldb else str(key)
                if index == -1:
                    self.put('name="%s",' % name)
                else:
                    self.put('key="[%s] %s",' % (index, name))
            self.putItem(value)
        else:
            self.putEmptyValue()
            self.putNumChild(2)
            self.putField("iname", self.currentIName)
            if self.isExpanded():
                with Children(self):
                    if self.pairData.useKeyAndValue:
                        self.putSubItem("key", key)
                        self.putSubItem("value", value)
                    else:
                        self.putSubItem("first", key)
                        self.putSubItem("second", value)

    def putPlainChildren(self, value, dumpBase = True):
        self.putEmptyValue(-99)
        self.putNumChild(1)
        if self.isExpanded():
            with Children(self):
                self.putFields(value, dumpBase)

    def putMembersItem(self, value, sortorder = 10):
        with SubItem(self, "[members]"):
            self.put('sortgroup="%s"' % sortorder)
            self.putPlainChildren(value)

    def isMapCompact(self, keyType, valueType):
        if self.currentItemFormat() == CompactMapFormat:
            return True
        return self.isSimpleType(keyType) and self.isSimpleType(valueType)

    def check(self, exp):
        if not exp:
            raise RuntimeError("Check failed")

    def checkRef(self, ref):
        count = self.extractInt(ref.address)
        # Assume there aren't a million references to any object.
        self.check(count >= -1)
        self.check(count < 1000000)

    def readToFirstZero(self, p, tsize, maximum):
        code = (None, "b", "H", None, "I")[tsize]
        base = toInteger(p)
        blob = self.extractBlob(base, maximum).toBytes()
        for i in xrange(0, maximum, tsize):
            t = struct.unpack_from(code, blob, i)[0]
            if t == 0:
                return 0, i, self.hexencode(blob[:i])

        # Real end is unknown.
        return -1, maximum, self.hexencode(blob[:maximum])

    def encodeCArray(self, p, tsize, limit):
        elided, shown, blob = self.readToFirstZero(p, tsize, limit)
        return elided, blob

    def putItemCount(self, count, maximum = 1000000000):
        # This needs to override the default value, so don't use 'put' directly.
        if count > maximum:
            self.putSpecialValue("minimumitemcount", maximum)
        else:
            self.putSpecialValue("itemcount", count)
        self.putNumChild(count)

    def resultToMi(self, value):
        if type(value) is bool:
            return '"%d"' % int(value)
        if type(value) is dict:
            return '{' + ','.join(['%s=%s' % (k, self.resultToMi(v))
                                for (k, v) in list(value.items())]) + '}'
        if type(value) is list:
            return '[' + ','.join([self.resultToMi(k)
                                for k in list(value.items())]) + ']'
        return '"%s"' % value

    def variablesToMi(self, value, prefix):
        if type(value) is bool:
            return '"%d"' % int(value)
        if type(value) is dict:
            pairs = []
            for (k, v) in list(value.items()):
                if k == 'iname':
                    if v.startswith('.'):
                        v = '"%s%s"' % (prefix, v)
                    else:
                        v = '"%s"' % v
                else:
                    v = self.variablesToMi(v, prefix)
                pairs.append('%s=%s' % (k, v))
            return '{' + ','.join(pairs) + '}'
        if type(value) is list:
            index = 0
            pairs = []
            for item in value:
                if item.get('type', '') == 'function':
                    continue
                name = item.get('name', '')
                if len(name) == 0:
                    name = str(index)
                    index += 1
                pairs.append((name, self.variablesToMi(item, prefix)))
            pairs.sort(key = lambda pair: pair[0])
            return '[' + ','.join([pair[1] for pair in pairs]) + ']'
        return '"%s"' % value

    def filterPrefix(self, prefix, items):
        return [i[len(prefix):] for i in items if i.startswith(prefix)]

    def tryFetchInterpreterVariables(self, args):
        if not int(args.get('nativemixed', 0)):
            return (False, '')
        context = args.get('context', '')
        if not len(context):
            return (False, '')

        expanded = args.get('expanded')
        args['expanded'] = self.filterPrefix('local', expanded)

        res = self.sendInterpreterRequest('variables', args)
        if not res:
            return (False, '')

        reslist = []
        for item in res.get('variables', {}):
            if not 'iname' in item:
                item['iname'] = '.' + item.get('name')
            reslist.append(self.variablesToMi(item, 'local'))

        watchers = args.get('watchers', None)
        if watchers:
            toevaluate = []
            name2expr = {}
            seq = 0
            for watcher in watchers:
                expr = self.hexdecode(watcher.get('exp'))
                name = str(seq)
                toevaluate.append({'name': name, 'expression': expr})
                name2expr[name] = expr
                seq += 1
            args['expressions'] = toevaluate

            args['expanded'] = self.filterPrefix('watch', expanded)
            del args['watchers']
            res = self.sendInterpreterRequest('expressions', args)

            if res:
                for item in res.get('expressions', {}):
                    name = item.get('name')
                    iname = 'watch.' + name
                    expr = name2expr.get(name)
                    item['iname'] = iname
                    item['wname'] = self.hexencode(expr)
                    item['exp'] = expr
                    reslist.append(self.variablesToMi(item, 'watch'))

        return (True, 'data=[%s]' % ','.join(reslist))

    def putField(self, name, value):
        self.put('%s="%s",' % (name, value))

    def putType(self, type, priority = 0):
        # Higher priority values override lower ones.
        if priority >= self.currentType.priority:
            self.currentType.value = str(type)
            self.currentType.priority = priority

    def putValue(self, value, encoding = None, priority = 0, elided = None):
        # Higher priority values override lower ones.
        # elided = 0 indicates all data is available in value,
        # otherwise it's the true length.
        if priority >= self.currentValue.priority:
            self.currentValue = ReportItem(value, encoding, priority, elided)

    def putSpecialValue(self, encoding, value = ""):
        self.putValue(value, encoding)

    def putEmptyValue(self, priority = -10):
        if priority >= self.currentValue.priority:
            self.currentValue = ReportItem("", None, priority, None)

    def putName(self, name):
        self.put('name="%s",' % name)

    def putBetterType(self, type):
        if isinstance(type, ReportItem):
            self.currentType.value = str(type.value)
        else:
            self.currentType.value = str(type)
        self.currentType.priority += 1

    def putNoType(self):
        # FIXME: replace with something that does not need special handling
        # in SubItem.__exit__().
        self.putBetterType(" ")

    def putInaccessible(self):
        #self.putBetterType(" ")
        self.putNumChild(0)
        self.currentValue.value = None

    def putNamedSubItem(self, component, value, name):
        with SubItem(self, component):
            self.putName(name)
            self.putItem(value)

    def isExpanded(self):
        #warn("IS EXPANDED: %s in %s: %s" % (self.currentIName,
        #    self.expandedINames, self.currentIName in self.expandedINames))
        return self.currentIName in self.expandedINames

    def putCStyleArray(self, value):
        arrayType = value.type.unqualified()
        innerType = value[0].type
        innerTypeName = str(innerType.unqualified())
        ts = innerType.sizeof

        try:
            self.putValue("@0x%x" % self.addressOf(value), priority = -1)
        except:
            self.putEmptyValue()
        self.putType(arrayType)

        try:
            p = self.addressOf(value)
        except:
            p = None

        displayFormat = self.currentItemFormat()
        arrayByteSize = arrayType.sizeof
        if arrayByteSize == 0:
            # This should not happen. But it does, see QTCREATORBUG-14755.
            # GDB/GCC produce sizeof == 0 for QProcess arr[3]
            s = str(value.type)
            itemCount = s[s.find('[')+1:s.find(']')]
            if not itemCount:
                itemCount = '100'
            arrayByteSize = int(itemCount) * ts;

        n = int(arrayByteSize / ts)
        if displayFormat != RawFormat and p:
            if innerTypeName == "char" or innerTypeName == "wchar_t":
                self.putCharArrayHelper(p, n, ts, self.currentItemFormat(),
                                        makeExpandable = False)
            else:
                self.tryPutSimpleFormattedPointer(p, arrayType, innerTypeName,
                    displayFormat, arrayByteSize)
        self.putNumChild(n)

        if self.isExpanded():
            self.putArrayData(p, n, innerType)

        self.putPlotDataHelper(p, n, innerType)

    def cleanAddress(self, addr):
        if addr is None:
            return "<no address>"
        # We cannot use str(addr) as it yields rubbish for char pointers
        # that might trigger Unicode encoding errors.
        #return addr.cast(lookupType("void").pointer())
        try:
            return "0x%x" % toInteger(hex(addr), 16)
        except:
            warn("CANNOT CONVERT TYPE: %s" % type(addr))
            try:
                warn("ADDR: %s" % addr)
            except:
                pass
            try:
                warn("TYPE: %s" % addr.type)
            except:
                pass
            return str(addr)

    def tryPutPrettyItem(self, typeName, value):
        if self.useFancy and self.currentItemFormat() != RawFormat:
            self.putType(typeName)

            nsStrippedType = self.stripNamespaceFromType(typeName)\
                .replace("::", "__")

            # The following block is only needed for D.
            if nsStrippedType.startswith("_A"):
                # DMD v2.058 encodes string[] as _Array_uns long long.
                # With spaces.
                if nsStrippedType.startswith("_Array_"):
                    qdump_Array(self, value)
                    return True
                if nsStrippedType.startswith("_AArray_"):
                    qdump_AArray(self, value)
                    return True

            dumper = self.qqDumpers.get(nsStrippedType)
            if not dumper is None:
                dumper(self, value)
                return True

            for pattern in self.qqDumpersEx.keys():
                dumper = self.qqDumpersEx[pattern]
                if re.match(pattern, nsStrippedType):
                    dumper(self, value)
                    return True

        return False

    def putSimpleCharArray(self, base, size = None):
        if size is None:
            elided, shown, data = self.readToFirstZero(base, 1, self.displayStringLimit)
        else:
            elided, shown = self.computeLimit(int(size), self.displayStringLimit)
            data = self.readMemory(base, shown)
        self.putValue(data, "latin1", elided=elided)

    def putDisplay(self, editFormat, value):
        self.put('editformat="%s",' % editFormat)
        self.put('editvalue="%s",' % value)

    # This is shared by pointer and array formatting.
    def tryPutSimpleFormattedPointer(self, value, typeName, innerTypeName, displayFormat, limit):
        if displayFormat == AutomaticFormat:
            if innerTypeName == "char":
                # Use UTF-8 as default for char *.
                self.putType(typeName)
                (elided, data) = self.encodeCArray(value, 1, limit)
                self.putValue(data, "utf8", elided=elided)
                return True

            if innerTypeName == "wchar_t":
                self.putType(typeName)
                charSize = self.lookupType('wchar_t').sizeof
                (elided, data) = self.encodeCArray(value, charSize, limit)
                if charSize == 2:
                    self.putValue(data, "utf16", elided=elided)
                else:
                    self.putValue(data, "ucs4", elided=elided)
                return True

        if displayFormat == Latin1StringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 1, limit)
            self.putValue(data, "latin1", elided=elided)
            return True

        if displayFormat == SeparateLatin1StringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 1, limit)
            self.putValue(data, "latin1", elided=elided)
            self.putDisplay("latin1:separate", data)
            return True

        if displayFormat == Utf8StringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 1, limit)
            self.putValue(data, "utf8", elided=elided)
            return True

        if displayFormat == SeparateUtf8StringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 1, limit)
            self.putValue(data, "utf8", elided=elided)
            self.putDisplay("utf8:separate", data)
            return True

        if displayFormat == Local8BitStringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 1, limit)
            self.putValue(data, "local8bit", elided=elided)
            return True

        if displayFormat == Utf16StringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 2, limit)
            self.putValue(data, "utf16", elided=elided)
            return True

        if displayFormat == Ucs4StringFormat:
            self.putType(typeName)
            (elided, data) = self.encodeCArray(value, 4, limit)
            self.putValue(data, "ucs4", elided=elided)
            return True

        return False

    def putFormattedPointer(self, value):
        #warn("POINTER: %s" % value)
        if self.isNull(value):
            #warn("NULL POINTER")
            self.putType(value.type)
            self.putValue("0x0")
            self.putNumChild(0)
            return

        typeName = str(value.type)

        (dereferencable, pointerValue) = self.pointerInfo(value)
        self.putAddress(pointerValue)
        self.putOriginalAddress(value)
        if not dereferencable:
            # Failure to dereference a pointer should at least
            # show the value of a pointer.
            self.putValue(self.cleanAddress(pointerValue))
            self.putType(typeName)
            self.putNumChild(0)
            return

        displayFormat = self.currentItemFormat(value.type)
        innerType = value.type.target().unqualified()
        innerTypeName = str(innerType)

        if innerTypeName == "void":
            #warn("VOID POINTER: %s" % displayFormat)
            self.putType(typeName)
            self.putValue(str(value))
            self.putNumChild(0)
            return

        if displayFormat == RawFormat:
            # Explicitly requested bald pointer.
            self.putType(typeName)
            self.putValue(self.hexencode(str(value)), "utf8:1:0")
            self.putNumChild(1)
            if self.currentIName in self.expandedINames:
                with Children(self):
                    with SubItem(self, '*'):
                        self.putItem(value.dereference())
            return

        limit = self.displayStringLimit
        if displayFormat == SeparateLatin1StringFormat \
                or displayFormat == SeparateUtf8StringFormat:
            limit = 1000000
        if self.tryPutSimpleFormattedPointer(value, typeName, innerTypeName, displayFormat, limit):
            self.putNumChild(0)
            return

        if Array10Format <= displayFormat and displayFormat <= Array1000Format:
            n = (10, 100, 1000, 10000)[displayFormat - Array10Format]
            self.putType(typeName)
            self.putItemCount(n)
            self.putArrayData(value, n, innerType)
            return

        if self.isFunctionType(innerType):
            # A function pointer.
            val = str(value)
            pos = val.find(" = ") # LLDB only, but...
            if pos > 0:
                val = val[pos + 3:]
            self.putValue(val)
            self.putType(innerTypeName)
            self.putNumChild(0)
            return

        #warn("AUTODEREF: %s" % self.autoDerefPointers)
        #warn("INAME: %s" % self.currentIName)
        if self.autoDerefPointers or self.currentIName.endswith('.this'):
            # Generic pointer type with AutomaticFormat.
            # Never dereference char types.
            if innerTypeName != "char" \
                    and innerTypeName != "signed char" \
                    and innerTypeName != "unsigned char"  \
                    and innerTypeName != "wchar_t":
                self.putType(innerTypeName)
                savedCurrentChildType = self.currentChildType
                self.currentChildType = self.stripClassTag(innerTypeName)
                self.putItem(value.dereference())
                self.currentChildType = savedCurrentChildType
                self.putOriginalAddress(value)
                return

        #warn("GENERIC PLAIN POINTER: %s" % value.type)
        #warn("ADDR PLAIN POINTER: 0x%x" % value.address)
        self.putType(typeName)
        self.putValue("0x%x" % self.pointerValue(value))
        self.putNumChild(1)
        if self.currentIName in self.expandedINames:
            with Children(self):
                with SubItem(self, "*"):
                    self.putItem(value.dereference())

    def putOriginalAddress(self, value):
        if not value.address is None:
            self.put('origaddr="0x%x",' % toInteger(value.address))

    def putQObjectNameValue(self, value):
        try:
            intSize = self.intSize()
            ptrSize = self.ptrSize()
            # dd = value["d_ptr"]["d"] is just behind the vtable.
            dd = self.extractPointer(value, offset=ptrSize)

            if self.qtVersion() < 0x050000:
                # Size of QObjectData: 5 pointer + 2 int
                #  - vtable
                #   - QObject *q_ptr;
                #   - QObject *parent;
                #   - QObjectList children;
                #   - uint isWidget : 1; etc..
                #   - int postedEvents;
                #   - QMetaObject *metaObject;

                # Offset of objectName in QObjectPrivate: 5 pointer + 2 int
                #   - [QObjectData base]
                #   - QString objectName
                objectName = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)

            else:
                # Size of QObjectData: 5 pointer + 2 int
                #   - vtable
                #   - QObject *q_ptr;
                #   - QObject *parent;
                #   - QObjectList children;
                #   - uint isWidget : 1; etc...
                #   - int postedEvents;
                #   - QDynamicMetaObjectData *metaObject;
                extra = self.extractPointer(dd + 5 * ptrSize + 2 * intSize)
                if extra == 0:
                    return False

                # Offset of objectName in ExtraData: 6 pointer
                #   - QVector<QObjectUserData *> userData; only #ifndef QT_NO_USERDATA
                #   - QList<QByteArray> propertyNames;
                #   - QList<QVariant> propertyValues;
                #   - QVector<int> runningTimers;
                #   - QList<QPointer<QObject> > eventFilters;
                #   - QString objectName
                objectName = self.extractPointer(extra + 5 * ptrSize)

            data, size, alloc = self.byteArrayDataHelper(objectName)

            # Object names are short, and GDB can crash on to big chunks.
            # Since this here is a convenience feature only, limit it.
            if size <= 0 or size > 80:
                return False

            raw = self.readMemory(data, 2 * size)
            self.putValue(raw, "utf16", 1)
            return True

        except:
        #    warn("NO QOBJECT: %s" % value.type)
            pass


    def extractMetaObjectPtr(self, objectPtr, typeobj):
        """ objectPtr - address of *potential* instance of QObject derived class
            typeobj - type of *objectPtr if known, None otherwise. """

        def canBePointer(p):
            if ptrSize == 4:
                return p > 100000 and (p & 0x3 == 0)
            else:
                return p > 100000 and (p & 0x7 == 0) and (p < 0x7fffffffffff)

        def couldBeQObject():
            try:
                (vtablePtr, dd) \
                    = self.extractStruct('PP', objectPtr, 2 * ptrSize)
            except:
                self.bump("nostruct-1")
                return False
            if not canBePointer(vtablePtr):
                self.bump("vtable")
                return False
            if not canBePointer(dd):
                self.bump("d_d_ptr")
                return False

            try:
                (dvtablePtr, qptr, parentPtr, childrenDPtr, flags) \
                    = self.extractStruct('PPPPI', dd, 4 * ptrSize + 4)
            except:
                self.bump("nostruct-2")
                return False
            #warn("STRUCT DD: %s %s" % (self.currentIName, x))
            if not canBePointer(dvtablePtr):
                self.bump("dvtable")
                #warn("DVT: 0x%x" % dvtablePtr)
                return False
            # Check d_ptr.d.q_ptr == objectPtr
            if qptr != objectPtr:
                #warn("QPTR: 0x%x 0x%x" % (qptr, objectPtr))
                self.bump("q_ptr")
                return False
            if parentPtr and not canBePointer(parentPtr):
                #warn("PAREN")
                self.bump("parent")
                return False
            if not canBePointer(childrenDPtr):
                #warn("CHILD")
                self.bump("children")
                return False
            #if flags >= 0x80: # Only 7 flags are defined
            #    warn("FLAGS: 0x%x %s" % (flags, self.currentIName))
            #    self.bump("flags")
            #    return False
            #warn("OK")
            #if dynMetaObjectPtr and not canBePointer(dynMetaObjectPtr):
            #    self.bump("dynmo")
            #    return False

            self.bump("couldBeQObject")
            return True

        def extractMetaObjectPtrFromAddress():
            return 0
            # FIXME: Calling "works" but seems to impact memory contents(!)
            # in relevant places. One symptom is that object name
            # contents "vanishes" as the reported size of the string
            # gets zeroed out(?).
            # Try vtable, metaObject() is the first entry.
            vtablePtr = self.extractPointer(objectPtr)
            metaObjectFunc = self.extractPointer(vtablePtr)
            cmd = "((void*(*)(void *))0x%x)(0x%x)" % (metaObjectFunc, objectPtr)
            try:
                #warn("MO CMD: %s" % cmd)
                res = self.parseAndEvaluate(cmd)
                #warn("MO RES: %s" % res)
                self.bump("successfulMetaObjectCall")
                return toInteger(res)
            except:
                self.bump("failedMetaObjectCall")
                #warn("COULD NOT EXECUTE: %s" % cmd)
            return 0

        def extractStaticMetaObjectFromTypeHelper(typeobj):
            if self.isSimpleType(typeobj):
                return 0

            typeName = str(typeobj)
            isQObjectProper = typeName == self.qtNamespace() + "QObject"

            if not isQObjectProper:
                if self.directBaseClass(typeobj, 0) is None:
                    return 0

                # No templates for now.
                if typeName.find('<') >= 0:
                    return 0

            result = self.findStaticMetaObject(typeName)

            # We need to distinguish Q_OBJECT from Q_GADGET:
            # a Q_OBJECT SMO has a non-null superdata (unless it's QObject itself),
            # a Q_GADGET SMO has a null superdata (hopefully)
            if result and not isQObjectProper:
                superdata = self.extractPointer(result)
                if superdata == 0:
                    # This looks like a Q_GADGET
                    return 0

            return result

        def extractStaticMetaObjectPtrFromType(someTypeObj):
            if someTypeObj is None:
                return 0
            someTypeName = str(someTypeObj)
            self.bump('metaObjectFromType')
            known = self.knownStaticMetaObjects.get(someTypeName, None)
            if known is not None: # Is 0 or the static metaobject.
                return known

            result = 0
            #try:
            result = extractStaticMetaObjectFromTypeHelper(someTypeObj)
            #except RuntimeError as error:
            #    warn("METAOBJECT EXTRACTION FAILED: %s" % error)
            #except:
            #    warn("METAOBJECT EXTRACTION FAILED FOR UNKNOWN REASON")

            if not result:
                base = self.directBaseClass(someTypeObj, 0)
                if base != someTypeObj: # sanity check
                    result = extractStaticMetaObjectPtrFromType(base)

            self.knownStaticMetaObjects[someTypeName] = result
            return result


        if not self.useFancy:
            return 0

        ptrSize = self.ptrSize()

        typeName = str(typeobj)
        result = self.knownStaticMetaObjects.get(typeName, None)
        if result is not None: # Is 0 or the static metaobject.
            self.bump("typecached")
            #warn("CACHED RESULT: %s %s 0x%x" % (self.currentIName, typeName, result))
            return result

        if not couldBeQObject():
            self.bump('cannotBeQObject')
            #warn("DOES NOT LOOK LIKE A QOBJECT: %s" % self.currentIName)
            return 0

        metaObjectPtr = 0
        if not metaObjectPtr:
            # measured: 3 ms (example had one level of inheritance)
            self.preping("metaObjectType-" + self.currentIName)
            metaObjectPtr = extractStaticMetaObjectPtrFromType(typeobj)
            self.ping("metaObjectType-" + self.currentIName)

        if not metaObjectPtr:
            # measured: 200 ms (example had one level of inheritance)
            self.preping("metaObjectCall-" + self.currentIName)
            metaObjectPtr = extractMetaObjectPtrFromAddress()
            self.ping("metaObjectCall-" + self.currentIName)

        #if metaObjectPtr:
        #    self.bump('foundMetaObject')
        #    self.knownStaticMetaObjects[typeName] = metaObjectPtr

        return metaObjectPtr

    def extractStruct(self, pattern, base, size):
        #warn("PATTERN: '%s'" % pattern)
        pointerReplacement = 'Q' if self.ptrSize() == 8 else 'I'
        pattern = pattern.replace('P', pointerReplacement)
        mem = self.readRawMemory(base, size)
        #warn("OUT: '%s'" % str(struct.unpack_from(pattern, mem)))
        return struct.unpack_from(pattern, mem)

    def extractCString(self, addr):
        result = bytearray()
        while True:
            d = self.extractByte(addr)
            if d == 0:
                break
            result.append(d)
            addr += 1
        return result

    def listChildrenGenerator(self, addr, innerType):
        base = self.extractPointer(addr)
        begin = self.extractInt(base + 8)
        end = self.extractInt(base + 12)
        array = base + 16
        if self.qtVersion() < 0x50000:
            array += self.ptrSize()
        size = end - begin
        innerSize = innerType.sizeof
        stepSize = self.ptrSize()
        addr = array + begin * stepSize
        isInternal = innerSize <= stepSize and self.isMovableType(innerType)
        for i in range(size):
            if isInternal:
                yield self.createValue(addr + i * stepSize, innerType)
            else:
                p = self.extractPointer(addr + i * stepSize)
                yield self.createValue(p, innerType)

    def vectorChildrenGenerator(self, addr, innerType):
        base = self.extractPointer(addr)
        size = self.extractInt(base + 4)
        data = base + self.extractPointer(base + 8 + self.ptrSize())
        innerSize = innerType.sizeof
        for i in range(size):
            yield self.createValue(data + i * innerSize, innerType)

    def putTypedPointer(self, name, addr, typeName):
        """ Prints a typed pointer, expandable if the type can be resolved,
            and without children otherwise """
        with SubItem(self, name):
            self.putAddress(addr)
            self.putValue("@0x%x" % addr)
            typeObj = self.lookupType(typeName)
            if typeObj:
                self.putType(typeObj)
                self.putNumChild(1)
                if self.isExpanded():
                    with Children(self):
                        self.putFields(self.createValue(addr, typeObj))
            else:
                self.putType(typeName)
                self.putNumChild(0)

    def putStructGuts(self, value):
        self.putEmptyValue()
        #metaObjectPtr = self.extractMetaObjectPtr(self.addressOf(value), value.type)
        if self.showQObjectNames:
            self.preping(self.currentIName)
            metaObjectPtr = self.extractMetaObjectPtr(self.addressOf(value), value.type)
            self.ping(self.currentIName)
            if metaObjectPtr:
                self.context = value
                self.putQObjectNameValue(value)
        #warn("STRUCT GUTS: %s  MO: 0x%x " % (self.currentIName, metaObjectPtr))
        if self.isExpanded():
            self.put('sortable="1"')
            with Children(self, 1, childType=None):
                self.putFields(value)
                if not self.showQObjectNames:
                    metaObjectPtr = self.extractMetaObjectPtr(self.addressOf(value), value.type)
                if metaObjectPtr:
                    self.putQObjectGuts(value, metaObjectPtr)

    # This is called is when a QObject derived class is expanded
    def putQObjectGuts(self, qobject, metaObjectPtr):
        self.putQObjectGutsHelper(qobject, self.addressOf(qobject), -1, metaObjectPtr, "QObject")


    def metaString(self, metaObjectPtr, index, revision = 7):
        #stringData = self.extractPointer(metaObjectPtr["d"]["stringdata"])
        ptrSize = self.ptrSize()
        stringdata = self.extractPointer(toInteger(metaObjectPtr) + ptrSize)
        if revision >= 7: # Qt 5.
            #byteArrayDataType = self.lookupQtType("QByteArrayData")
            #byteArrayDataSize = byteArrayDataType.sizeof
            byteArrayDataSize = 24 if ptrSize == 8 else 16
            literal = stringdata + toInteger(index) * byteArrayDataSize
            ldata, lsize, lalloc = self.byteArrayDataHelper(literal)
            try:
                return self.extractBlob(ldata, lsize).toString()
            except:
                return "<not available>"
        else: # Qt 4.
            ldata = stringdata + index
            return self.extractCString(ldata).decode("utf8")

    def putQMetaStuff(self, value, origType):
        metaObjectPtr = value["mobj"]
        if toInteger(metaObjectPtr):
            handle = toInteger(value["handle"])
            index = toInteger(metaObjectPtr["d"]["data"][handle])
            name = self.metaString(metaObjectPtr, index)
            self.putValue(name)
            self.putNumChild(1)
            if self.isExpanded():
                with Children(self):
                    self.putFields(value)
                    self.putQObjectGutsHelper(0, 0, handle, toInteger(metaObjectPtr), origType)
        else:
            self.putEmptyValue()
            if self.isExpanded():
                with Children(self):
                    self.putFields(value)

    # basically all meta things go through this here.
    # qobject and qobjectPtr are non-null  if coming from a real structure display
    # qobject == 0, qobjectPtr != 0 is possible for builds without QObject debug info
    #   if qobject == 0, properties and d-ptr cannot be shown.
    # handle is what's store in QMetaMethod etc, pass -1 for QObject/QMetaObject itself
    # metaObjectPtr needs to point to a valid QMetaObject.
    def putQObjectGutsHelper(self, qobject, qobjectPtr, handle, metaObjectPtr, origType):
        intSize = self.intSize()
        ptrSize = self.ptrSize()

        def putt(name, value, typeName = ' '):
            with SubItem(self, name):
                self.putValue(value)
                self.putType(typeName)
                self.putNumChild(0)

        def extractSuperDataPtr(someMetaObjectPtr):
            #return someMetaObjectPtr['d']['superdata']
            return self.extractPointer(someMetaObjectPtr)

        def extractDataPtr(someMetaObjectPtr):
            # dataPtr = metaObjectPtr["d"]["data"]
            return self.extractPointer(someMetaObjectPtr + 2 * ptrSize)

        isQMetaObject = origType == "QMetaObject"
        isQObject = origType == "QObject"

        #warn("OBJECT GUTS: %s 0x%x " % (self.currentIName, metaObjectPtr))
        dataPtr = extractDataPtr(metaObjectPtr)
        #warn("DATA PTRS: %s 0x%x " % (self.currentIName, dataPtr))
        (revision, classname,
            classinfo, classinfo2,
            methodCount, methods,
            propertyCount, properties,
            enumCount, enums,
            constructorCount, constructors,
            flags, signalCount) = self.extractStruct('IIIIIIIIIIIIII', dataPtr, 56)

        largestStringIndex = -1
        for i in range(methodCount):
            t = self.extractStruct('IIIII', dataPtr + 56 + i * 20, 20)
            if largestStringIndex < t[0]:
                largestStringIndex = t[0]

        extraData = 0
        if qobjectPtr:
            #isQt5 = self.qtVersion() >= 0x50000
            #extraDataOffset = 5 * ptrSize + 8 if isQt5 else 6 * ptrSize + 8
            # (QObjectData/QObjectPrivate) dd = value["d_ptr"]["d"] is just behind the vtable.
            dd = self.extractPointer(qobjectPtr + ptrSize)
            if self.qtVersion() >= 0x50000:
                (dvtablePtr, qptr, parentPtr, childrenDPtr, flags, postedEvents, dynMetaObjectPtr,
                    extraData, threadDataPtr, connectionListsPtr, sendersPtr, currentSenderPtr) \
                        = self.extractStruct('PPPPIIPPPPPP', dd, 10 * ptrSize + 8)
            else:
                (dvtablePtr, qptr, parentPtr, childrenDPtr, flags, postedEvents, dynMetaObjectPtr,
                    extraData, threadDataPtr, connectionListsPtr, sendersPtr, currentSenderPtr) \
                        = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

        if qobjectPtr:
            qobjectType = self.lookupQtType("QObject")
            badType = qobjectType is None
            with SubItem(self, "[parent]"):
                self.put('sortgroup="9"')
                parentPtrType = self.voidPtrType() if badType else qobjectType.pointer()
                self.putItem(self.createValue(dd + 2 * ptrSize, parentPtrType))
            with SubItem(self, "[children]"):
                self.put('sortgroup="8"')
                base = self.extractPointer(dd + 3 * ptrSize) # It's a QList<QObject *>
                begin = self.extractInt(base + 8)
                end = self.extractInt(base + 12)
                array = base + 16
                if self.qtVersion() < 0x50000:
                    array += ptrSize
                self.check(begin >= 0 and end >= 0 and end <= 1000 * 1000 * 1000)
                size = end - begin
                self.check(size >= 0)
                self.putItemCount(size)
                if self.isExpanded():
                    addrBase = array + begin * ptrSize
                    with Children(self, size):
                        for i in self.childRange():
                            with SubItem(self, i):
                                childPtr = self.extractPointer(addrBase + i * ptrSize)
                                if badType:
                                    # release build
                                    childMetaObjectPtr = self.extractMetaObjectPtr(childPtr, None)
                                    if childMetaObjectPtr:
                                        # release build + live process
                                        self.putNumChild(1)
                                        self.putAddress(childPtr)
                                        if self.isExpanded():
                                            with Children(self):
                                                self.putQObjectGutsHelper(0, childPtr, -1, childMetaObjectPtr, "QObject")
                                    else:
                                        # release build + core dump
                                        self.putItem(self.createValue(addrBase + i * ptrSize, self.voidPtrType()))
                                else:
                                    # debug info
                                    self.putItem(self.createValue(childPtr, qobjectType))

        if isQMetaObject:
            with SubItem(self, "[strings]"):
                self.put('sortgroup="2"')
                if largestStringIndex > 0:
                    self.putSpecialValue("minimumitemcount", largestStringIndex)
                    self.putNumChild(1)
                    if self.isExpanded():
                        with Children(self, largestStringIndex + 1):
                            for i in self.childRange():
                                with SubItem(self, i):
                                    self.putValue(self.hexencode(self.metaString(metaObjectPtr, i)), "latin1")
                                    self.putNumChild(0)
                else:
                    self.putValue(" ")
                    self.putNumChild(0)

        if isQMetaObject:
            with SubItem(self, "[raw]"):
                self.put('sortgroup="1"')
                self.putEmptyValue()
                self.putNumChild(1)
                if self.isExpanded():
                    with Children(self):
                        putt("revision", revision)
                        putt("classname", classname)
                        putt("classinfo", classinfo)
                        putt("methods", "%d %d" % (methodCount, methods))
                        putt("properties", "%d %d" % (propertyCount, properties))
                        putt("enums/sets", "%d %d" % (enumCount, enums))
                        putt("constructors", "%d %d" % (constructorCount, constructors))
                        putt("flags", flags)
                        putt("signalCount", signalCount)
                        for i in range(methodCount):
                            t = self.extractStruct('IIIII', dataPtr + 56 + i * 20, 20)
                            putt("method %d" % i, "%s %s %s %s %s" % t)

        if isQObject:
            with SubItem(self, "[extra]"):
                self.put('sortgroup="1"')
                self.putEmptyValue()
                self.putNumChild(1)
                if self.isExpanded():
                    with Children(self):
                        if extraData:
                            self.putTypedPointer("[extraData]", extraData,
                                                 self.qtNamespace() + "QObjectPrivate::ExtraData")

                        if connectionListsPtr:
                            self.putTypedPointer("[connectionLists]", connectionListsPtr,
                                                 self.qtNamespace() + "QObjectConnectionListVector")

                        with SubItem(self, "[metaObject]"):
                            self.putAddress(metaObjectPtr)
                            self.putNumChild(1)
                            if self.isExpanded():
                                with Children(self):
                                    self.putQObjectGutsHelper(0, 0, -1, metaObjectPtr, "QMetaObject")


        if isQMetaObject or isQObject:
            with SubItem(self, "[properties]"):
                self.put('sortgroup="5"')
                if self.isExpanded():
                    usesVector = self.qtVersion() >= 0x50700
                    dynamicPropertyCount = 0
                    with Children(self):
                        # Static properties.
                        for i in range(propertyCount):
                            t = self.extractStruct("III", dataPtr + properties * 4 + 12 * i, 12)
                            name = self.metaString(metaObjectPtr, t[0])
                            if qobject:
                                self.putCallItem(name, qobject, "property", '"' + name + '"')
                            else:
                                putt(name, ' ')

                        # Dynamic properties.
                        if extraData:
                            byteArrayType = self.lookupQtType("QByteArray")
                            variantType = self.lookupQtType("QVariant")
                            names = self.listChildrenGenerator(extraData + ptrSize, byteArrayType)
                            if usesVector:
                                values = self.vectorChildrenGenerator(extraData + 2 * ptrSize, variantType)
                            else:
                                values = self.listChildrenGenerator(extraData + 2 * ptrSize, variantType)
                            for (k, v) in zip(names, values):
                                with SubItem(self, propertyCount + dynamicPropertyCount):
                                    self.put('key="%s",' % self.encodeByteArray(k))
                                    self.put('keyencoded="latin1",')
                                    self.putItem(v)
                                    dynamicPropertyCount += 1
                    self.putItemCount(propertyCount + dynamicPropertyCount)
                else:
                    # We need a handle to [x] for the user to expand the item
                    # before we know whether there are actual children. Counting
                    # them is too expensive.
                    self.putSpecialValue("minimumitemcount", propertyCount)
                    self.putNumChild(1)

        superDataPtr = extractSuperDataPtr(metaObjectPtr)

        globalOffset = 0
        superDataIterator = superDataPtr
        while superDataIterator:
            sdata = extractDataPtr(superDataIterator)
            globalOffset += self.extractInt(sdata + 16) # methodCount member
            superDataIterator = extractSuperDataPtr(superDataIterator)

        if isQMetaObject or isQObject:
            with SubItem(self, "[methods]"):
                self.put('sortgroup="3"')
                self.putItemCount(methodCount)
                if self.isExpanded():
                    with Children(self):
                        for i in range(methodCount):
                            t = self.extractStruct("IIIII", dataPtr + 56 + 20 * i, 20)
                            name = self.metaString(metaObjectPtr, t[0])
                            with SubItem(self, i):
                                self.putValue(name)
                                self.putType(" ")
                                self.putNumChild(1)
                                isSignal = False
                                flags = t[4]
                                if flags == 0x06:
                                    typ = "signal"
                                    isSignal = True
                                elif flags == 0x0a:
                                    typ = "slot"
                                elif flags == 0x0a:
                                    typ = "invokable"
                                else:
                                    typ = "<unknown>"
                                with Children(self):
                                    putt("[nameindex]", t[0])
                                    putt("[type]", typ)
                                    putt("[argc]", t[1])
                                    putt("[parameter]", t[2])
                                    putt("[tag]", t[3])
                                    putt("[flags]", t[4])
                                    putt("[localindex]", str(i))
                                    putt("[globalindex]", str(globalOffset + i))

        if isQObject:
            with SubItem(self, "[d]"):
                self.put('sortgroup="15"')
                try:
                    qobjectPrivateType = self.lookupQtType("QObjectPrivate")
                    self.putItem(qobject["d_ptr"]["d"].dereference().cast(qobjectPrivateType))
                except:
                    try:
                        self.putItem(qobject["d_ptr"]["d"])
                    except:
                        self.putNumChild(0)
                        self.putType(self.qtNamespace() + "QObjectData *")
                        self.putSpecialValue("notaccessible")

        if isQMetaObject:
            with SubItem(self, "[superdata]"):
                self.put('sortgroup="12"')
                if superDataPtr:
                    self.putType(self.qtNamespace() + "QMetaObject")
                    self.putAddress(superDataPtr)
                    self.putNumChild(1)
                    if self.isExpanded():
                        with Children(self):
                            self.putQObjectGutsHelper(0, 0, -1, superDataPtr, "QMetaObject")
                else:
                    self.putType(self.qtNamespace() + "QMetaObject *")
                    self.putValue("0x0")
                    self.putNumChild(0)

        if handle >= 0:
            localIndex = int((handle - methods) / 5)
            with SubItem(self, "[localindex]"):
                self.put('sortgroup="12"')
                self.putValue(localIndex)
            with SubItem(self, "[globalindex]"):
                self.put('sortgroup="11"')
                self.putValue(globalOffset + localIndex)


        #with SubItem(self, "[signals]"):
        #    self.putItemCount(signalCount)
        #    signalNames = metaData(52, -14, 5)
        #    warn("NAMES: %s" % signalNames)
        #    if self.isExpanded():
        #        with Children(self):
        #            putt("A", "b")
        #            for i in range(signalCount):
        #                k = signalNames[i]
        #                with SubItem(self, k):
        #                    self.putEmptyValue()
        #            if dd:
        #                self.putQObjectConnections(dd)

    def putQObjectConnections(self, dd):
        with SubItem(self, "[connections]"):
            ptrSize = self.ptrSize()
            self.putNoType()
            ns = self.qtNamespace()
            privateTypeName = ns + "QObjectPrivate"
            privateType = self.lookupType(privateTypeName)
            d_ptr = dd.cast(privateType.pointer()).dereference()
            connections = d_ptr["connectionLists"]
            if self.isNull(connections):
                self.putItemCount(0)
            else:
                connections = connections.dereference()
                connections = connections.cast(self.directBaseClass(connections.type))
                self.putSpecialValue("minimumitemcount", 0)
                self.putNumChild(1)
            if self.isExpanded():
                pp = 0
                with Children(self):
                    innerType = self.templateArgument(connections.type, 0)
                    # Should check:  innerType == ns::QObjectPrivate::ConnectionList
                    base = self.extractPointer(connections)
                    data, size, alloc = self.vectorDataHelper(base)
                    connectionType = self.lookupType(ns + "QObjectPrivate::Connection")
                    for i in xrange(size):
                        first = self.extractPointer(data + i * 2 * ptrSize)
                        while first:
                            self.putSubItem("%s" % pp,
                                self.createPointerValue(first, connectionType))
                            first = self.extractPointer(first + 3 * ptrSize)
                            # We need to enforce some upper limit.
                            pp += 1
                            if pp > 1000:
                                break

    def isKnownMovableType(self, typeName):
        if typeName in (
                "QBrush", "QBitArray", "QByteArray", "QCustomTypeInfo", "QChar", "QDate",
                "QDateTime", "QFileInfo", "QFixed", "QFixedPoint", "QFixedSize",
                "QHashDummyValue", "QIcon", "QImage", "QLine", "QLineF", "QLatin1Char",
                "QLocale", "QMatrix", "QModelIndex", "QPoint", "QPointF", "QPen",
                "QPersistentModelIndex", "QResourceRoot", "QRect", "QRectF", "QRegExp",
                "QSize", "QSizeF", "QString", "QTime", "QTextBlock", "QUrl", "QVariant",
                "QXmlStreamAttribute", "QXmlStreamNamespaceDeclaration",
                "QXmlStreamNotationDeclaration", "QXmlStreamEntityDeclaration"
                ):
            return True

        return typeName == "QStringList" and self.qtVersion() >= 0x050000

    def currentItemFormat(self, type = None):
        displayFormat = self.formats.get(self.currentIName, AutomaticFormat)
        if displayFormat == AutomaticFormat:
            if type is None:
                type = self.currentType.value
            needle = self.stripForFormat(str(type))
            displayFormat = self.typeformats.get(needle, AutomaticFormat)
        return displayFormat

    def putArrayData(self, base, n, innerType,
            childNumChild = None, maxNumChild = 10000):
        addrBase = toInteger(base)
        innerSize = innerType.sizeof
        enc = self.simpleEncoding(innerType)
        if enc:
            self.put('childtype="%s",' % innerType)
            self.put('addrbase="0x%x",' % addrBase)
            self.put('addrstep="0x%x",' % innerSize)
            self.put('arrayencoding="%s",' % enc)
            if n > maxNumChild:
                self.put('childrenelided="%s",' % n) # FIXME: Act on that in frontend
                n = maxNumChild
            self.put('arraydata="')
            self.put(self.readMemory(addrBase, n * innerSize))
            self.put('",')
        else:
            with Children(self, n, innerType, childNumChild, maxNumChild,
                    addrBase=addrBase, addrStep=innerSize):
                for i in self.childRange():
                    self.putSubItem(i, self.createValue(addrBase + i * innerSize, innerType))

    def putArrayItem(self, name, addr, n, typeName):
        with SubItem(self, name):
            self.putEmptyValue()
            self.putType("%s [%d]" % (typeName, n))
            self.putArrayData(addr, n, self.lookupType(typeName))
            self.putAddress(addr)

    def putPlotDataHelper(self, base, n, innerType, maxNumChild = 1000*1000):
        if n > maxNumChild:
            self.put('plotelided="%s",' % n) # FIXME: Act on that in frontend
            n = maxNumChild
        if self.currentItemFormat() == ArrayPlotFormat and self.isSimpleType(innerType):
            enc = self.simpleEncoding(innerType)
            if enc:
                self.putField("editencoding", enc)
                self.putDisplay("plotdata:separate",
                                self.readMemory(base, n * innerType.sizeof))

    def putPlotData(self, base, n, innerType, maxNumChild = 1000*1000):
        self.putPlotDataHelper(base, n, innerType, maxNumChild=maxNumChild)
        if self.isExpanded():
            self.putArrayData(base, n, innerType, maxNumChild=maxNumChild)

    def putSpecialArgv(self, value):
        """
        Special handling for char** argv.
        """
        n = 0
        p = value
        # p is 0 for "optimized out" cases. Or contains rubbish.
        try:
            if not self.isNull(p):
                while not self.isNull(p.dereference()) and n <= 100:
                    p += 1
                    n += 1
        except:
            pass

        with TopLevelItem(self, 'local.argv'):
            self.put('iname="local.argv",name="argv",')
            self.putItemCount(n, 100)
            self.putType('char **')
            if self.currentIName in self.expandedINames:
                p = value
                with Children(self, n):
                    for i in xrange(n):
                        self.putSubItem(i, p.dereference())
                        p += 1

    def extractPointer(self, thing, offset = 0):
        if isinstance(thing, int):
            rawBytes = self.extractBlob(thing, self.ptrSize()).toBytes()
        elif sys.version_info[0] == 2 and isinstance(thing, long):
            rawBytes = self.extractBlob(thing, self.ptrSize()).toBytes()
        elif isinstance(thing, Blob):
            rawBytes = thing.toBytes()
        else:
            # Assume it's a (backend specific) Value.
            rawBytes = self.toBlob(thing).toBytes()
        code = "I" if self.ptrSize() == 4 else "Q"
        return struct.unpack_from(code, rawBytes, offset)[0]


    # Parses a..b and  a.(s).b
    def parseRange(self, exp):

        # Search for the first unbalanced delimiter in s
        def searchUnbalanced(s, upwards):
            paran = 0
            bracket = 0
            if upwards:
                open_p, close_p, open_b, close_b = '(', ')', '[', ']'
            else:
                open_p, close_p, open_b, close_b = ')', '(', ']', '['
            for i in range(len(s)):
                c = s[i]
                if c == open_p:
                    paran += 1
                elif c == open_b:
                    bracket += 1
                elif c == close_p:
                    paran -= 1
                    if paran < 0:
                        return i
                elif c == close_b:
                    bracket -= 1
                    if bracket < 0:
                        return i
            return len(s)

        match = re.search("(\.)(\(.+?\))?(\.)", exp)
        if match:
            s = match.group(2)
            left_e = match.start(1)
            left_s =  1 + left_e - searchUnbalanced(exp[left_e::-1], False)
            right_s = match.end(3)
            right_e = right_s + searchUnbalanced(exp[right_s:], True)
            template = exp[:left_s] + '%s' +  exp[right_e:]

            a = exp[left_s:left_e]
            b = exp[right_s:right_e]

            try:
                # Allow integral expressions.
                ss = toInteger(self.parseAndEvaluate(s[1:len(s)-1]) if s else 1)
                aa = toInteger(self.parseAndEvaluate(a))
                bb = toInteger(self.parseAndEvaluate(b))
                if aa < bb and ss > 0:
                    return True, aa, ss, bb + 1, template
            except:
                pass
        return False, 0, 1, 1, exp

    def putNumChild(self, numchild):
        if numchild != self.currentChildNumChild:
            self.put('numchild="%s",' % numchild)

    def handleWatches(self, args):
        for watcher in args.get("watchers", []):
            iname = watcher['iname']
            exp = self.hexdecode(watcher['exp'])
            self.handleWatch(exp, exp, iname)

    def handleWatch(self, origexp, exp, iname):
        exp = str(exp).strip()
        escapedExp = self.hexencode(exp)
        #warn("HANDLING WATCH %s -> %s, INAME: '%s'" % (origexp, exp, iname))

        # Grouped items separated by semicolon
        if exp.find(";") >= 0:
            exps = exp.split(';')
            n = len(exps)
            with TopLevelItem(self, iname):
                self.put('iname="%s",' % iname)
                #self.put('wname="%s",' % escapedExp)
                self.put('name="%s",' % exp)
                self.put('exp="%s",' % exp)
                self.putItemCount(n)
                self.putNoType()
            for i in xrange(n):
                self.handleWatch(exps[i], exps[i], "%s.%d" % (iname, i))
            return

        # Special array index: e.g a[1..199] or a[1.(3).199] for stride 3.
        isRange, begin, step, end, template = self.parseRange(exp)
        if isRange:
            #warn("RANGE: %s %s %s in %s" % (begin, step, end, template))
            r = range(begin, end, step)
            n = len(r)
            with TopLevelItem(self, iname):
                self.put('iname="%s",' % iname)
                #self.put('wname="%s",' % escapedExp)
                self.put('name="%s",' % exp)
                self.put('exp="%s",' % exp)
                self.putItemCount(n)
                self.putNoType()
                with Children(self, n):
                    for i in r:
                        e = template % i
                        self.handleWatch(e, e, "%s.%s" % (iname, i))
            return

            # Fall back to less special syntax
            #return self.handleWatch(origexp, exp, iname)

        with TopLevelItem(self, iname):
            self.put('iname="%s",' % iname)
            self.put('wname="%s",' % escapedExp)
            try:
                value = self.parseAndEvaluate(exp)
                self.putItem(value)
            except RuntimeError:
                self.currentType.value = " "
                self.currentValue.value = "<no such value>"
                self.currentChildNumChild = -1
                self.currentNumChild = 0
                self.putNumChild(0)

    def registerDumper(self, funcname, function):
        try:
            if funcname.startswith("qdump__"):
                typename = funcname[7:]
                spec = inspect.getargspec(function)
                if len(spec.args) == 2:
                    self.qqDumpers[typename] = function
                elif len(spec.args) == 3 and len(spec.defaults) == 1:
                    self.qqDumpersEx[spec.defaults[0]] = function
                self.qqFormats[typename] = self.qqFormats.get(typename, [])
            elif funcname.startswith("qform__"):
                typename = funcname[7:]
                try:
                    self.qqFormats[typename] = function()
                except:
                    self.qqFormats[typename] = []
            elif funcname.startswith("qedit__"):
                typename = funcname[7:]
                try:
                    self.qqEditable[typename] = function
                except:
                    pass
        except:
            pass

    def setupDumpers(self, _ = {}):
        self.resetCaches()

        for mod in self.dumpermodules:
            m = __import__(mod)
            dic = m.__dict__
            for name in dic.keys():
                item = dic[name]
                self.registerDumper(name, item)

        msg = "dumpers=["
        for key, value in self.qqFormats.items():
            editable = ',editable="true"' if key in self.qqEditable else ''
            formats = (',formats=\"%s\"' % str(value)[1:-1]) if len(value) else ''
            msg += '{type="%s"%s%s},' % (key, editable, formats)
        msg += '],'
        v = 10000 * sys.version_info[0] + 100 * sys.version_info[1] + sys.version_info[2]
        msg += 'python="%d"' % v
        return msg

    def reloadDumpers(self, args):
        for mod in self.dumpermodules:
            m = sys.modules[mod]
            if sys.version_info[0] >= 3:
                import importlib
                importlib.reload(m)
            else:
                reload(m)
        self.setupDumpers(args)

    def addDumperModule(self, args):
        path = args['path']
        (head, tail) = os.path.split(path)
        sys.path.insert(1, head)
        self.dumpermodules.append(os.path.splitext(tail)[0])

    def extractQStringFromQDataStream(self, buf, offset):
        """ Read a QString from the stream """
        size = struct.unpack_from("!I", buf, offset)[0]
        offset += 4
        string = buf[offset:offset + size].decode('utf-16be')
        return (string, offset + size)

    def extractQByteArrayFromQDataStream(self, buf, offset):
        """ Read a QByteArray from the stream """
        size = struct.unpack_from("!I", buf, offset)[0]
        offset += 4
        string = buf[offset:offset + size].decode('latin1')
        return (string, offset + size)

    def extractIntFromQDataStream(self, buf, offset):
        """ Read an int from the stream """
        value = struct.unpack_from("!I", buf, offset)[0]
        return (value, offset + 4)

    def handleInterpreterMessage(self):
        """ Return True if inferior stopped """
        resdict = self.fetchInterpreterResult()
        return resdict.get('event') == 'break'

    def reportInterpreterResult(self, resdict, args):
        print('interpreterresult=%s,token="%s"'
            % (self.resultToMi(resdict), args.get('token', -1)))

    def reportInterpreterAsync(self, resdict, asyncclass):
        print('interpreterasync=%s,asyncclass="%s"'
            % (self.resultToMi(resdict), asyncclass))

    def removeInterpreterBreakpoint(self, args):
        res = self.sendInterpreterRequest('removebreakpoint', { 'id' : args['id'] })
        return res

    def insertInterpreterBreakpoint(self, args):
        args['condition'] = self.hexdecode(args.get('condition', ''))
        # Will fail if the service is not yet up and running.
        response = self.sendInterpreterRequest('setbreakpoint', args)
        resdict = args.copy()
        bp = None if response is None else response.get("breakpoint", None)
        if bp:
            resdict['number'] = bp
            resdict['pending'] = 0
        else:
            self.createResolvePendingBreakpointsHookBreakpoint(args)
            resdict['number'] = -1
            resdict['pending'] = 1
            resdict['warning'] = 'Direct interpreter breakpoint insertion failed.'
        self.reportInterpreterResult(resdict, args)

    def resolvePendingInterpreterBreakpoint(self, args):
        self.parseAndEvaluate('qt_qmlDebugEnableService("NativeQmlDebugger")')
        response = self.sendInterpreterRequest('setbreakpoint', args)
        bp = None if response is None else response.get("breakpoint", None)
        resdict = args.copy()
        if bp:
            resdict['number'] = bp
            resdict['pending'] = 0
        else:
            resdict['number'] = -1
            resdict['pending'] = 0
            resdict['error'] = 'Pending interpreter breakpoint insertion failed.'
        self.reportInterpreterAsync(resdict, 'breakpointmodified')

    def fetchInterpreterResult(self):
        buf = self.parseAndEvaluate("qt_qmlDebugMessageBuffer")
        size = self.parseAndEvaluate("qt_qmlDebugMessageLength")
        msg = self.hexdecode(self.readMemory(buf, size))
        # msg is a sequence of 'servicename<space>msglen<space>msg' items.
        resdict = {}  # Native payload.
        while len(msg):
            pos0 = msg.index(' ') # End of service name
            pos1 = msg.index(' ', pos0 + 1) # End of message length
            service = msg[0:pos0]
            msglen = int(msg[pos0+1:pos1])
            msgend = pos1+1+msglen
            payload = msg[pos1+1:msgend]
            msg = msg[msgend:]
            if service == 'NativeQmlDebugger':
                try:
                    resdict = json.loads(payload)
                    continue
                except:
                    warn("Cannot parse native payload: %s" % payload)
            else:
                print('interpreteralien=%s'
                    % {'service': service, 'payload': self.hexencode(payload)})
        try:
            expr = 'qt_qmlDebugClearBuffer()'
            res = self.parseAndEvaluate(expr)
        except RuntimeError as error:
            warn("Cleaning buffer failed: %s: %s" % (expr, error))

        return resdict

    def sendInterpreterRequest(self, command, args = {}):
        encoded = json.dumps({ 'command': command, 'arguments': args })
        hexdata = self.hexencode(encoded)
        expr = 'qt_qmlDebugSendDataToService("NativeQmlDebugger","%s")' % hexdata
        try:
            res = self.parseAndEvaluate(expr)
        except RuntimeError as error:
            warn("Interpreter command failed: %s: %s" % (encoded, error))
            return {}
        except AttributeError as error:
            # Happens with LLDB and 'None' current thread.
            warn("Interpreter command failed: %s: %s" % (encoded, error))
            return {}
        if not res:
            warn("Interpreter command failed: %s " % encoded)
            return {}
        return self.fetchInterpreterResult()

    def executeStep(self, args):
        if self.nativeMixed:
            response = self.sendInterpreterRequest('stepin', args)
        self.doContinue()

    def executeStepOut(self, args):
        if self.nativeMixed:
            response = self.sendInterpreterRequest('stepout', args)
        self.doContinue()

    def executeNext(self, args):
        if self.nativeMixed:
            response = self.sendInterpreterRequest('stepover', args)
        self.doContinue()

    def executeContinue(self, args):
        if self.nativeMixed:
            response = self.sendInterpreterRequest('continue', args)
        self.doContinue()

    def doInsertInterpreterBreakpoint(self, args, wasPending):
        #warn("DO INSERT INTERPRETER BREAKPOINT, WAS PENDING: %s" % wasPending)
        # Will fail if the service is not yet up and running.
        response = self.sendInterpreterRequest('setbreakpoint', args)
        bp = None if response is None else response.get("breakpoint", None)
        if wasPending:
            if not bp:
                self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
                    'error': 'Pending interpreter breakpoint insertion failed.'}, args)
                return
        else:
            if not bp:
                self.reportInterpreterResult({'bpnr': -1, 'pending': 1,
                    'warning': 'Direct interpreter breakpoint insertion failed.'}, args)
                self.createResolvePendingBreakpointsHookBreakpoint(args)
                return
        self.reportInterpreterResult({'bpnr': bp, 'pending': 0}, args)

    def isInternalInterpreterFrame(self, functionName):
        if functionName is None:
            return False
        if functionName.startswith("qt_v4"):
            return True
        return functionName.startswith(self.qtNamespace() + "QV4::")

    # Hack to avoid QDate* dumper timeouts with GDB 7.4 on 32 bit
    # due to misaligned %ebx in SSE calls (qstring.cpp:findChar)
    def canCallLocale(self):
        return True

    def isReportableInterpreterFrame(self, functionName):
        return functionName and functionName.find("QV4::Moth::VME::exec") >= 0

    def extractQmlData(self, value):
        if value.type.code == PointerCode:
            value = value.dereference()
        data = value["data"]
        return data.cast(self.lookupType(str(value.type).replace("QV4::", "QV4::Heap::")))

    # Contains iname, name, and value.
    class LocalItem:
        pass

    def extractInterpreterStack(self):
        return self.sendInterpreterRequest('backtrace', {'limit': 10 })

