diff --git a/doc/images/qml-profiler-flamegraph.png b/doc/images/qml-profiler-flamegraph.png index 23c6558028f..4e6c694385b 100644 Binary files a/doc/images/qml-profiler-flamegraph.png and b/doc/images/qml-profiler-flamegraph.png differ diff --git a/doc/images/qmldesigner-binding-editor.png b/doc/images/qmldesigner-binding-editor.png new file mode 100644 index 00000000000..7c8edfd90d1 Binary files /dev/null and b/doc/images/qmldesigner-binding-editor.png differ diff --git a/doc/images/qmldesigner-set-expression.png b/doc/images/qmldesigner-set-expression.png index 4d1ad5152bc..bbcbd094425 100644 Binary files a/doc/images/qmldesigner-set-expression.png and b/doc/images/qmldesigner-set-expression.png differ diff --git a/doc/images/qmldesigner-transitions.png b/doc/images/qmldesigner-transitions.png index 66624e93d26..f3599b7ca66 100644 Binary files a/doc/images/qmldesigner-transitions.png and b/doc/images/qmldesigner-transitions.png differ diff --git a/doc/images/qtcreator-qml-performance-monitor.png b/doc/images/qtcreator-qml-performance-monitor.png index f8e17dc5e95..6de351818e2 100644 Binary files a/doc/images/qtcreator-qml-performance-monitor.png and b/doc/images/qtcreator-qml-performance-monitor.png differ diff --git a/doc/src/analyze/cpu-usage-analyzer.qdoc b/doc/src/analyze/cpu-usage-analyzer.qdoc index 873abff8c08..dddea53102f 100644 --- a/doc/src/analyze/cpu-usage-analyzer.qdoc +++ b/doc/src/analyze/cpu-usage-analyzer.qdoc @@ -346,11 +346,11 @@ \li Some versions of Perf will not start recording unless given a certain minimum sampling frequency. Try with a \uicontrol {Sampling Frequency} of 1000. - \li On some devices, for example Boundary Devices i.MX6 Boards, the - Perf support is not very stable and the Linux kernel may randomly - fail to record data after some time. Perf can use different types - of events to trigger samples. You can get a list of available event - types by running \c {perf list} on the device and add + \li On some devices, in particular various i.MX6 Boards, the hardware + performance counters are dysfunctional and the Linux kernel may + randomly fail to record data after some time. Perf can use different + types of events to trigger samples. You can get a list of available + event types by running \c {perf list} on the device and add \c {-e } to the \uicontrol {Additional arguments} field to change the event type to be used. The choice of event type affects the performance and stability of the sampling. diff --git a/doc/src/analyze/qtquick-profiler.qdoc b/doc/src/analyze/qtquick-profiler.qdoc index 141ca00fcb0..fa73ac388b3 100644 --- a/doc/src/analyze/qtquick-profiler.qdoc +++ b/doc/src/analyze/qtquick-profiler.qdoc @@ -171,23 +171,25 @@ recorded. Move the cursor on an event on a row to see how long it takes and where in the source it is being called. To display the information only when an event is selected, disable the \uicontrol {View Event Information on Mouseover} - button (5). + button (4). The outline (10) summarizes the period for which data was collected. Drag the zoom range (8) or click the outline to move on the outline. You can - also move between events by selecting the \uicontrol {Jump to Previous Event} (1) - and \uicontrol {Jump to Next Event} (2) buttons. + also move between events by selecting the \uicontrol {Jump to Previous Event} + and \uicontrol {Jump to Next Event} buttons (1). - Select the \uicontrol {Show Zoom Slider} button (3) to open a slider that you can + Select the \uicontrol {Show Zoom Slider} button (2) to open a slider that you can use to set the zoom level. You can also drag the zoom handles (9). To reset the default zoom level, right-click the timeline to open the context menu, and select \uicontrol {Reset Zoom}. + Click the time ruler to add vertical orientation lines (5) to the timeline. + \section2 Selecting Event Ranges You can select an event range (7) to view the frame rate of events and to compare it with the frame rate of similar events. Select the - \uicontrol {Select Range} button (4) to activate the selection tool. Then click in + \uicontrol {Select Range} button (3) to activate the selection tool. Then click in the timeline to specify the beginning of the event range. Drag the selection handle to define the end of the range. The length of the range indicates the frame rate of the event. @@ -557,14 +559,26 @@ \section2 Visualizing Statistics as Flame Graphs The \uicontrol {Flame Graph} view shows a more concise statistical overview - of QML and JavaScript execution. The horizontal bars show the amount of + of QML and JavaScript execution. In the \uicontrol {Visualize Total Time} + view, the horizontal bars show the amount of time all invocations of a certain function took together, relative to the total runtime of all JavaScript and QML events. The nesting shows which - functions were called by which other ones. Mind that, unlike the + functions were called by which other ones. + + \image qml-profiler-flamegraph.png "Flame Graph View" + + To view the total amount of memory allocated by the functions in the + \uicontrol {Visualize Memory} view, select \uicontrol Memory in the + drop-down menu (1). + + To view the the number of memory allocations performed by the functions in + the \uicontrol {Visualize Allocations} view, select \uicontrol Allocations + in the drop-down menu. + + Unlike the \uicontrol Timeline view, the \uicontrol {Flame Graph} view does not show the time spans when no QML or JavaScript is running at all. Thus, it is not suitable for analyzing per frame execution times. However, it is very easy to see the total impact of the various QML and JavaScript events there. - \image qml-profiler-flamegraph.png "Flame Graph View" */ diff --git a/doc/src/editors/creator-diff-editor.qdoc b/doc/src/editors/creator-diff-editor.qdoc index 33e6250fb28..a0286ae8615 100644 --- a/doc/src/editors/creator-diff-editor.qdoc +++ b/doc/src/editors/creator-diff-editor.qdoc @@ -37,22 +37,32 @@ \title Comparing Files - You can use a diff editor from \QC to compare two text files. They can be - either versions of the same file or arbitrary files. + You can use a diff editor to display unsaved changed in the current file or + all open files or to compare any two text files that can be either versions + of the same file or arbitrary files. - \image creator-diff-editor.png "Diff editor output in the Edit mode" + To view unsaved changes in the current file, select \uicontrol Tools > + \uicontrol Diff > \uicontrol {Diff Current File}. - To compare files: + To view unsaved changes in all open files, select \uicontrol Tools > + \uicontrol Diff > \uicontrol {Diff Open Files}. + + To compare two files: \list 1 - \li Select \uicontrol Tools > \uicontrol Diff. + \li Select \uicontrol Tools > \uicontrol Diff > + \uicontrol {Diff External Files}. \li Select two files to compare. \endlist - The differences are output in the \uicontrol Edit mode. Color coding is used + \section1 Viewing the Changes + + \image creator-diff-editor.png "Diff editor output in the Edit mode" + + The changes are displayed in the \uicontrol Edit mode. Color coding is used to mark changed lines. By default, light red indicates lines that contain removed text (painted a darker red) in the left pane and light green indicates lines that contain added text (painted a darker green) in the @@ -70,6 +80,26 @@ \inlineimage sidebysidediff.png (\uicontrol {Switch to Side by Side Diff Editor}). + To only show text changes, select \uicontrol {Ignore Whitespace}. + + To expand the context for the changes, set the number of unchanged lines to + show in \uicontrol {Context lines}. Set the value to -1 to show the whole + file. + + By default, the horizontal scroll bars in the left and right pane are + synchronized. To use them independently of each other, select the + \inlineimage linkicon.png + (\uicontrol {Synchronize Horizontal Scroll Bars}) button. + + If the files change outside \QC, select \inlineimage reload_gray.png + (\uicontrol {Reload Editor}) to compare them again and to show the results. + + To send a chunk of changes to a \l{Pasting and Fetching Code Snippets} + {code pasting service}, select \uicontrol {Send Chunk to CodePaster} in the + context menu. + + \section1 Changing the Colors + To change the default colors, select \uicontrol Tools > \uicontrol Options > \uicontrol {Text Editor} > \uicontrol {Font & Colors}. Create your own color scheme and select new colors for the following options: @@ -95,22 +125,4 @@ indicates added characters. \endlist - - To only show text changes, select \uicontrol {Ignore Whitespace}. - - To expand the context for the changes, set the number of unchanged lines to - show in \uicontrol {Context lines}. Set the value to -1 to show the whole - file. - - By default, the horizontal scroll bars in the left and right pane are - synchronized. To use them independently of each other, select the - \inlineimage linkicon.png - (\uicontrol {Synchronize Horizontal Scroll Bars}) button. - - If the files change outside \QC, select \inlineimage reload_gray.png - (\uicontrol {Reload Editor}) to compare them again and to show the results. - - To send a chunk of changes to a \l{Pasting and Fetching Code Snippets} - {code pasting service}, select \uicontrol {Send Chunk to CodePaster} in the - context menu. */ diff --git a/doc/src/editors/creator-editors.qdoc b/doc/src/editors/creator-editors.qdoc index 3ccaaf3eb88..594956a3e79 100644 --- a/doc/src/editors/creator-editors.qdoc +++ b/doc/src/editors/creator-editors.qdoc @@ -915,6 +915,9 @@ \li C++ code snippets, which specify C++ code constructs + \li CMake code snippets that you can use when editing \c CMakeLists.txt + files in the CMake editor + \li QML code snippets, which specify QML code constructs \li Nim code snippets, which specify Nim code constructs diff --git a/doc/src/projects/creator-projects-cmake.qdoc b/doc/src/projects/creator-projects-cmake.qdoc index 525c10eb389..bf542beb3f5 100644 --- a/doc/src/projects/creator-projects-cmake.qdoc +++ b/doc/src/projects/creator-projects-cmake.qdoc @@ -161,6 +161,8 @@ \li Keyword completion + \li Code completion + \li Auto-indentation \li Matching parentheses and quotes diff --git a/doc/src/qtquick/qtquick-designer.qdoc b/doc/src/qtquick/qtquick-designer.qdoc index 7a143d24c4a..81d7ebcfa24 100644 --- a/doc/src/qtquick/qtquick-designer.qdoc +++ b/doc/src/qtquick/qtquick-designer.qdoc @@ -428,6 +428,10 @@ you can change the position of an object on the canvas and then add animation to the change between the states. + To determine when the state should be applied, select + \uicontrol {Set when Condition} in the context menu and specify a + \l [QtQuick]{State::when}{when} property for the state. + You can preview the states in the \uicontrol State pane and click them to switch between states on the canvas. diff --git a/doc/src/qtquick/qtquick-screens.qdoc b/doc/src/qtquick/qtquick-screens.qdoc index 726f3179ec2..2709590f251 100644 --- a/doc/src/qtquick/qtquick-screens.qdoc +++ b/doc/src/qtquick/qtquick-screens.qdoc @@ -133,12 +133,23 @@ \image qmldesigner-set-expression.png "Type properties context menu" + The \uicontrol {Binding Editor} supports code completion. Start typing a + string and press \key Ctrl+Space to display a list of properties, IDs, and + code snippets. When you enter a period (.) after a property name, a list of + available values is displayed. Press \key Enter to accept the first + suggestion in the list and to complete the code. + + \image qmldesigner-binding-editor.png "Binding Editor" + To remove bindings, select \uicontrol Reset in the context menu. + You can set bindings also in the \uicontrol Connections view. For more + information, see \l {Adding Bindings Between Properties}. + For more information on the JavaScript environment provided by QML, see \l{Integrating QML and JavaScript}. - \QMLD cannot show bindings and using them might have a negative impact on + Bindings are a black box for \QMLD and using them might have a negative impact on performance, so consider setting anchors and margins for items, instead. For example, instead of setting \c {parent.width} for an item, you could anchor the item to its sibling items on the left and the right. diff --git a/scripts/createSourcePackages.py b/scripts/createSourcePackages.py index d6e89a85fa3..af99704ac3f 100755 --- a/scripts/createSourcePackages.py +++ b/scripts/createSourcePackages.py @@ -78,7 +78,7 @@ def package_repos(repos, combined_prefix, target_file_base): print('Creating .tar.gz...') createTarGz(archive_path(crlf=False), target_file_base + '.tar.gz') print('Creating .tar.xz...') - createTarGz(archive_path(crlf=False), target_file_base + '.tar.xz') + createTarXz(archive_path(crlf=False), target_file_base + '.tar.xz') print('Creating .zip with CRLF...') createZip(archive_path(crlf=True), target_file_base + '.zip') print('Removing temporary directory...') diff --git a/share/qtcreator/debugger/boosttypes.py b/share/qtcreator/debugger/boosttypes.py index af64c6df7fc..938a0a29670 100644 --- a/share/qtcreator/debugger/boosttypes.py +++ b/share/qtcreator/debugger/boosttypes.py @@ -117,25 +117,25 @@ def qdump__boost__unordered__unordered_set(d, value): innerType = value.type[0] bucketCount = d.extractInt(base + ptrSize) #warn("A BUCKET COUNT: %s" % bucketCount) - #warn("X BUCKET COUNT: %s" % d.parseAndEvaluate("s1.table_.bucket_count_")) + #warn("X BUCKET COUNT: %s" % d.parseAndEvaluate("s1.table_.bucket_count_").value()) try: # boost 1.58 table = value["table_"] bucketsAddr = table["buckets_"].integer() #warn("A BUCKETS: 0x%x" % bucketsAddr) - #warn("X BUCKETS: %s" % d.parseAndEvaluate("s1.table_.buckets_")) + #warn("X BUCKETS: 0x%x" % d.parseAndEvaluate("s1.table_.buckets_").pointer()) lastBucketAddr = bucketsAddr + bucketCount * ptrSize #warn("A LAST BUCKET: 0x%x" % lastBucketAddr) - #warn("X LAST BUCKET: %s" % d.parseAndEvaluate("s1.table_.get_bucket(s1.table_.bucket_count_)")) + #warn("X LAST BUCKET: 0x%x" % d.parseAndEvaluate("s1.table_.get_bucket(s1.table_.bucket_count_)").pointer()) previousStartAddr = lastBucketAddr #warn("A PREVIOUS START: 0x%x" % previousStartAddr) - #warn("X PREVIOUS START: %s" % d.parseAndEvaluate("s1.table_.get_previous_start()")) + #warn("X PREVIOUS START: 0x%x" % d.parseAndEvaluate("s1.table_.get_previous_start()").pointer()) item = d.extractPointer(previousStartAddr) #warn("A KEY ADDR: 0x%x" % item) - #warn("X KEY ADDR: %s" % d.parseAndEvaluate("s1.table_.get_previous_start()->next_")) + #warn("X KEY ADDR: 0x%x" % d.parseAndEvaluate("s1.table_.get_previous_start()->next_").pointer()) item = d.extractPointer(previousStartAddr) #warn("A VALUE: %x" % d.extractInt(item + ptrSize)) - #warn("X VALUE: %s" % d.parseAndEvaluate("*(int*)(s1.table_.get_previous_start()->next_ + 1)")) + #warn("X VALUE: %x" % d.parseAndEvaluate("*(int*)(s1.table_.get_previous_start()->next_ + 1)").integer()) with Children(d, size, maxNumChild=10000): for j in d.childRange(): d.putSubItem(j, d.createValue(item + 2 * ptrSize, innerType)) diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index c0264a1fdb3..79026875ee2 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -217,13 +217,16 @@ class PairedChildrenData: 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 class PairedChildren(Children): def __init__(self, d, numChild, useKeyAndValue = False, pairType = None, keyType = None, valueType = None, maxNumChild = None): self.d = d + if pairType is not None: + try: + pairType = pairType.stripTypedefs() + except: + pass if keyType is None: keyType = pairType[0].unqualified() if valueType is None: @@ -233,7 +236,6 @@ class PairedChildren(Children): d.pairData.vname = 'value' if useKeyAndValue else 'second' Children.__init__(self, d, numChild, - d.pairData.childType, maxNumChild = maxNumChild, addrBase = None, addrStep = None) @@ -286,6 +288,8 @@ class DumperBase: self.qtNamespaceToReport = None self.passExceptions = False + self.typeData = {} + self.resetCaches() self.resetStats() @@ -416,7 +420,6 @@ class DumperBase: self.currentValue = ReportItem(); self.currentType = ReportItem(); - def exitSubItem(self, item, exType, exValue, exTraceBack): #warn('CURRENT VALUE: %s: %s %s' % # (self.currentIName, self.currentValue, self.currentType)) @@ -510,6 +513,71 @@ class DumperBase: self.ptrSize = lambda: result return result + def lookupType(self, typeName): + nativeType = self.lookupNativeType(typeName) + return None if nativeType is None else self.fromNativeType(nativeType) + + def listTemplateParameters(self, typename): + targs = [] + if not typename.endswith('>'): + return targs + + def push(inner): + # Handle local struct definitions like QList + inner = inner.strip()[::-1] + p = inner.find(')::') + if p > -1: + inner = inner[p+3:].strip() + if inner.startswith('const '): + inner = inner[6:].strip() + if inner.endswith(' const'): + inner = inner[:-6].strip() + #warn("FOUND: %s" % inner) + targs.append(inner) + + #warn("SPLITTING %s" % typename) + level = 0 + inner = '' + for c in typename[::-1]: # Reversed... + #warn("C: %s" % c) + if c == '>': + if level > 0: + inner += c + level += 1 + elif c == '<': + level -= 1 + if level > 0: + inner += c + else: + push(inner) + inner = '' + elif c == ',': + #warn('c: %s level: %s' % (c, level)) + if level == 1: + push(inner) + inner = '' + else: + inner += c + else: + inner += c + + #warn("TARGS: %s %s" % (typename, targs)) + res = [] + for item in targs[::-1]: + c = ord(item[0]) + if c == '-' or (c >= 48 and c < 58): + if item.find('.') > -1: + res.append(float(item)) + else: + val = toInteger(item) + if val > 0x80000000: + val -= 0x100000000 + res.append(val) + else: + res.append(self.Type(self, item)) + #warn("RES: %s %s" % (typename, [(None if t is None else t.name) for t in res])) + return res + # Hex decoding operating on str, return str. def hexdecode(self, s): if sys.version_info[0] == 2: @@ -822,11 +890,6 @@ class DumperBase: self.putField('sortgroup', sortorder) self.putPlainChildren(value) - def isMapCompact(self, keyType, valueType): - if self.currentItemFormat() == CompactMapFormat: - return True - return keyType.isSimpleType() and valueType.isSimpleType() - def check(self, exp): if not exp: error('Check failed: %s' % exp) @@ -846,8 +909,7 @@ class DumperBase: self.checkIntType(maximum) code = (None, 'b', 'H', None, 'I')[tsize] - #blob = self.readRawMemory(base, maximum) - + #blob = self.readRawMemory(base, 1) blob = bytes() while maximum > 1: try: @@ -1064,7 +1126,7 @@ class DumperBase: itemCount = '100' arrayByteSize = int(itemCount) * innerType.size(); - n = int(arrayByteSize / innerType.size()) + n = arrayByteSize // innerType.size() p = value.address() if displayFormat != RawFormat and p: if innerType.name in ('char', 'wchar_t'): @@ -1212,8 +1274,9 @@ class DumperBase: return False def putFormattedPointer(self, value): + #warn("PUT FORMATTED: %s" % value) pointer = value.pointer() - #warn('POINTER: %s' % pointer) + #warn('POINTER: 0x%x' % pointer) if pointer == 0: #warn('NULL POINTER') self.putType(value.type) @@ -1223,8 +1286,7 @@ class DumperBase: typeName = value.type.name - self.putAddress(pointer) - self.putOriginalAddress(value) + self.putAddress(value.address()) try: self.readRawMemory(pointer, 1) @@ -1238,7 +1300,7 @@ class DumperBase: return displayFormat = self.currentItemFormat(value.type.name) - innerType = value.type.target().unqualified() + innerType = value.type.target() #.unqualified() if innerType.name == 'void': #warn('VOID POINTER: %s' % displayFormat) @@ -1284,19 +1346,21 @@ class DumperBase: #warn('INAME: %s' % self.currentIName) #warn('INNER: %s' % innerType.name) if self.autoDerefPointers or self.currentIName.endswith('.this'): - # Generic pointer type with AutomaticFormat. + derefValue = value.dereference() # Never dereference char types. if innerType.name not in ('char', 'signed char', 'unsigned char', 'wchar_t'): + # Generic pointer type with AutomaticFormat. self.putType(innerType) savedCurrentChildType = self.currentChildType self.currentChildType = innerType.name - self.putItem(value.dereference()) + derefValue.name = '*' + self.putItem(derefValue) self.currentChildType = savedCurrentChildType - self.putOriginalAddress(value) + self.putOriginalAddress(pointer) return #warn('GENERIC PLAIN POINTER: %s' % value.type) - #warn('ADDR PLAIN POINTER: 0x%x' % value.address) + #warn('ADDR PLAIN POINTER: 0x%x' % value.laddress) self.putType(typeName) self.putValue('0x%x' % pointer) self.putNumChild(1) @@ -1305,9 +1369,9 @@ class DumperBase: with SubItem(self, '*'): self.putItem(value.dereference()) - def putOriginalAddress(self, value): - if value.address() is not None: - self.put('origaddr="0x%x",' % value.address()) + def putOriginalAddress(self, address): + if address is not None: + self.put('origaddr="0x%x",' % address) def putQObjectNameValue(self, value): try: @@ -1938,12 +2002,12 @@ class DumperBase: # Should check: innerType == ns::QObjectPrivate::ConnectionList base = self.extractPointer(connections) data, size, alloc = self.vectorDataHelper(base) - connectionType = self.createType('@QObjectPrivate::Connection*') + connectionType = self.createType('@QObjectPrivate::Connection') for i in xrange(size): first = self.extractPointer(data + i * 2 * ptrSize) while first: self.putSubItem('%s' % pp, - self.createValue(first, connectionType)) + self.createPointerValue(first, connectionType)) first = self.extractPointer(first + 3 * ptrSize) # We need to enforce some upper limit. pp += 1 @@ -1974,7 +2038,8 @@ class DumperBase: self.checkIntType(n) addrBase = base innerSize = innerType.size() - #warn('ADDRESS: %s INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType)) + self.putNumChild(n) + #warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType)) enc = innerType.simpleEncoding() if enc: self.put('childtype="%s",' % innerType.name) @@ -2466,7 +2531,7 @@ class DumperBase: return False def putItem(self, value): - #warn('ITEM: %s' % value.stringify()) + #warn('PUT ITEM: %s' % value.stringify()) typeobj = value.type #unqualified() typeName = typeobj.name @@ -2489,8 +2554,9 @@ class DumperBase: return if typeobj.code == TypeCodeTypedef: - strippedType = typeobj.stripTypedefs() - self.putItem(value.cast(strippedType)) + #warn('TYPEDEF VALUE: %s' % value.stringify()) + #strippedType = typeobj.ltarget + self.putItem(value.detypedef()) if value.lbitsize is not None and value.lbitsize != value.type.size() * 8: typeName += ' : %s' % value.lbitsize self.putBetterType(typeName) @@ -2535,13 +2601,10 @@ class DumperBase: return if typeobj.code == TypeCodeReference: - target = typeobj.target() - if self.isLldb: - item = value.cast(target.pointer()).dereference() - else: - item = value.cast(target.unqualified()) - self.putItem(item) - self.putBetterType(target.name + ' &') + #warn('REFERENCE VALUE: %s' % value) + val = value.dereference() + self.putItem(val) + self.putBetterType(typeName) return if typeobj.code == TypeCodeComplex: @@ -2574,8 +2637,8 @@ class DumperBase: self.putType(typeName) self.putNumChild(1) self.putEmptyValue() - #warn('STRUCT GUTS: %s ADDRESS: %s ' % (value.name, value.address())) - #metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) + #warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address())) + metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) if self.showQObjectNames: self.preping(self.currentIName) metaObjectPtr = self.extractMetaObjectPtr(value.address(), value.type) @@ -2607,9 +2670,6 @@ class DumperBase: return tiVersion return None - def lookupType(self, typestring): - return self.fromNativeType(self.lookupNativeType(typestring)) - def addToCache(self, typeobj): typename = typeobj.name if typename in self.typesReported: @@ -2622,11 +2682,12 @@ class DumperBase: self.dumper = dumper self.name = None self.type = None - self.ldata = None - self.laddress = None + self.ldata = None # Target address in case of references and pointers. + self.laddress = None # Own address. self.lIsInScope = True self.ldisplay = None self.lbitsize = None + self.targetValue = None # For references. def check(self): if self.laddress is not None and not self.dumper.isInt(self.laddress): @@ -2649,7 +2710,8 @@ class DumperBase: def display(self): if self.type.code == TypeCodeEnum: - return self.type.enumDisplay(self.extractInteger(self.type.bitsize(), False)) + intval = self.extractInteger(self.type.bitsize(), False) + return self.type.typeData.enumDisplay(intval) simple = self.value() if simple is not None: return str(simple) @@ -2663,30 +2725,38 @@ class DumperBase: return 'value of type %s at address 0x%x' % (self.type.name, self.laddress) return '' + def pointer(self): + if self.type.code == TypeCodeTypedef: + return self.detypedef().pointer() + return self.extractInteger(self.dumper.ptrSize() * 8, True) + def integer(self): - unsigned = self.type.stripTypedefs().name.startswith('unsigned') + if self.type.code == TypeCodeTypedef: + return self.detypedef().integer() + unsigned = self.type.name.startswith('unsigned') bitsize = self.type.bitsize() return self.extractInteger(bitsize, unsigned) def floatingPoint(self): + if self.type.code == TypeCodeTypedef: + return self.detypedef().floatingPoint() if self.type.size() == 8: return self.extractSomething('d', 64) if self.type.size() == 4: return self.extractSomething('f', 32) error('BAD FLOAT DATA: %s SIZE: %s' % (self, self.type.size())) - def pointer(self): - return self.extractInteger(8 * self.dumper.ptrSize(), True) - def value(self): if self.type is not None: + if self.type.code == TypeCodeTypedef: + return self.detypedef().value() if self.type.code == TypeCodeIntegral: return self.integer() if self.type.code == TypeCodeFloat: return self.floatingPoint() if self.type.code == TypeCodeTypedef: - return self.cast(self.type.stripTypedefs()).value() - if self.type.stripTypedefs().code == TypeCodePointer: + return self.cast(self.type.ltarget).value() + if self.type.code == TypeCodePointer: return self.pointer() return None @@ -2697,8 +2767,8 @@ class DumperBase: #warn('GET ITEM %s %s' % (self, index)) self.check() if self.type.code == TypeCodeTypedef: - #warn('GET ITEM %s STRIP TYPEDEFS TO %s' % (self, self.type.stripTypedefs())) - return self.cast(self.type.stripTypedefs()).__getitem__(index) + #warn('GET ITEM STRIP TYPEDEFS TO %s' % self.type.ltarget) + return self.cast(self.type.ltarget).__getitem__(index) if isinstance(index, str): if self.type.code == TypeCodePointer: #warn('GET ITEM %s DEREFERENCE TO %s' % (self, self.dereference())) @@ -2725,7 +2795,9 @@ class DumperBase: def extractField(self, field): if self.type.code == TypeCodeTypedef: - return self.cast(self.type.stripTypedefs()).extractField(field) + return self.cast(self.type.ltarget).extractField(field) + if self.type.code == TypeCodeReference: + return self.dereference().extractField(field) if not isinstance(field, self.dumper.Field): error('BAD INDEX TYPE %s' % type(field)) @@ -2756,15 +2828,12 @@ class DumperBase: val.type = None if val.laddress is not None and fieldType is not None: - if fieldType.code in (TypeCodePointer, TypeCodeReference): - baseType = fieldType.dereference() - address = self.dumper.extractPointer(val.laddress) - dynTypeName = self.dumper.dynamicTypeName(baseType, address) - if dynTypeName is not None: - if fieldType.code == TypeCodePointer: - val.type = self.dumper.createType(dynTypeName + '*') - else: - val.type = self.dumper.createType(dynTypeName + ' &') + if fieldType.code == TypeCodePointer: + objectAddress = self.dumper.extractPointer(val.laddress) + val = self.dumper.createPointerValue(objectAddress, fieldType.ltarget) + elif fieldType.code == TypeCodeReference: + objectAddress = self.dumper.extractPointer(val.laddress) + val = self.dumper.createReferenceValue(objectAddress, fieldType.ltarget) if val.type is None: val.type = fieldType @@ -2795,27 +2864,54 @@ class DumperBase: def dereference(self): self.check() + if self.type.code == TypeCodeTypedef: + return self.detypedef().dereference() val = self.dumper.Value(self.dumper) - val.type = self.type.dereference() - val.laddress = self.pointer() - dynTypeName = self.dumper.dynamicTypeName(val.type, val.laddress) - if dynTypeName is not None: - val.type = self.dumper.createType(dynTypeName) + if self.type.code == TypeCodeReference: + val.laddress = self.pointer() + if val.laddress is None and self.laddress is not None: + val.laddress = self.laddress + val.type = self.type.dereference().dynamicType(val.laddress) + elif self.type.code == TypeCodePointer: + val.laddress = self.pointer() + val.type = self.type.dereference().dynamicType(val.laddress) + else: + error("WRONG: %s" % self.type.code) + #warn("DEREFERENCING FROM: %s" % self) + #warn("DEREFERENCING TO: %s" % val) + #dynTypeName = val.type.dynamicTypeName(val.laddress) + #if dynTypeName is not None: + # val.type = self.dumper.createType(dynTypeName) + return val + + def detypedef(self): + self.check() + if self.type.code != TypeCodeTypedef: + error("WRONG") + val = self.dumper.Value(self.dumper) + val.type = self.type.ltarget + val.ldata = self.ldata + val.laddress = self.laddress + #warn("DETYPEDEF FROM: %s" % self) + #warn("DETYPEDEF TO: %s" % val) return val def extend(self, size): if self.type.size() < size: val = self.dumper.Value(self.dumper) val.laddress = None - if sys.version_info[0] == 3: - val.ldata = self.ldata + bytes('\0' * (size - self.type.size()), encoding='latin1') - else: - val.ldata = self.ldata + bytes('\0' * (size - self.type.size())) + val.ldata = self.zeroExtend(self.ldata) return val if self.type.size() == size: return self error('NOT IMPLEMENTED') + def zeroExtend(self, data, size): + if sys.version_info[0] == 3: + return data + bytes('\0' * (size - len(data)), encoding='latin1') + else: + return data + bytes('\0' * (size - len(data))) + def cast(self, typish): self.check() val = self.dumper.Value(self.dumper) @@ -2843,12 +2939,15 @@ class DumperBase: return self.ldata if size < len(self.ldata): return self.ldata[:size] + #error('ZERO-EXTENDING DATA TO %s BYTES: %s' % (size, self)) + return self.zeroExtend(self.ldata, size) if self.laddress is not None: if size is None: size = self.type.size() res = self.dumper.readRawMemory(self.laddress, size) if len(res) > 0: return res + error('CANNOT CONVERT ADDRESS TO BYTES: %s' % self) error('CANNOT CONVERT TO BYTES: %s' % self) def extractInteger(self, bitsize, unsigned): @@ -2892,8 +2991,8 @@ class DumperBase: if field.isStruct: if field.ltype != field.fieldType(): error('DO NOT SIMPLIFY') - #warn('FIELD POS: %s' % field.ltype) - #warn('FIELD TYE: %s' % field.fieldType()) + #warn('FIELD POS: %s' % field.ltype.stringify()) + #warn('FIELD TYE: %s' % field.fieldType().stringify()) res = self.dumper.createValue(thing, field.fieldType()) #warn('RES TYPE: %s' % res.type) if self.laddress is not None: @@ -2908,75 +3007,151 @@ class DumperBase: ptr = p if self.isInt(p) else p.pointer() self.readRawMemory(ptr, 1) - def dynamicTypeName(self, baseType, address): - if baseType.code != TypeCodeStruct: - return None - try: - vtbl = self.extractPointer(address) - except: - return None - #warn('VTBL: 0x%x' % vtbl) - if not self.canBePointer(vtbl): - return None - return self.nativeDynamicTypeName(address, baseType) + def type(self, typeId): + return self.typeData.get(typeId) - class Type: + def registerType(self, typeId, tdata): + #warn('REGISTER TYPE: %s' % typeId) + self.typeData[typeId] = tdata + #typeId = typeId.replace(' ', '') + #self.typeData[typeId] = tdata + #warn('REGISTERED: %s' % self.typeData) + + def registerTypeAlias(self, existingTypeId, aliasId): + #warn('REGISTER ALIAS %s FOR %s' % (aliasId, existingTypeId)) + self.typeData[aliasId] = self.typeData[existingTypeId] + + class TypeData: def __init__(self, dumper): self.dumper = dumper - self.name = None - self.nativeType = None - self.lfields = None + self.lfields = [] self.lbitsize = None self.lbitpos = None self.ltarget = None # Inner type for arrays - self.templateArguments = None + self.templateArguments = [] self.code = None + self.name = None + self.typeId = None + self.enumDisplay = str + + class Type: + def __init__(self, dumper, typeId): + self.typeId = typeId + self.dumper = dumper def __str__(self): - self.check() - error('Not implemented') - return self.name - #error('Not implemented') + #return self.typeId + return self.stringify() + + @property + def typeData(self): + tdata = self.dumper.typeData.get(self.typeId, None) + if tdata is not None: + #warn('USING : %s' % self.typeId) + return tdata + typeId = self.typeId.replace(' ', '') + if tdata is not None: + #warn('USING FALLBACK : %s' % self.typeId) + return tdata + #warn('EXPANDING LAZILY: %s' % self.typeId) + self.dumper.lookupType(self.typeId) + return self.dumper.typeData.get(self.typeId) + + @property + def name(self): + tdata = self.typeData + if tdata is None: + return self.typeId + return tdata.name + + @property + def code(self): + return self.typeData.code + + @property + def lbitsize(self): + return self.typeData.lbitsize + + @property + def lbitpos(self): + return self.typeData.lbitpos + + @property + def ltarget(self): + return self.typeData.ltarget + + @property + def lfields(self): + return self.typeData.lfields def stringify(self): - return 'Type(name="%s",bsize=%s,bpos=%s,code=%s,ntype=%s)' \ - % (self.name, self.lbitsize, self.lbitpos, self.code, self.nativeType) + tdata = self.typeData + if tdata is None: + return 'Type(id="%s")' % self.typeId + return 'Type(name="%s",bsize=%s,bpos=%s,code=%s)' \ + % (tdata.name, tdata.lbitsize, tdata.lbitpos, tdata.code) def __getitem__(self, index): if self.dumper.isInt(index): return self.templateArgument(index) error('CANNOT INDEX TYPE') + def dynamicTypeName(self, address): + tdata = self.typeData + if tdata is None: + return None + if tdata.code != TypeCodeStruct: + return None + try: + vtbl = self.dumper.extractPointer(address) + except: + return None + #warn('VTBL: 0x%x' % vtbl) + if not self.dumper.canBePointer(vtbl): + return None + return self.dumper.nativeDynamicTypeName(address, self) + + def dynamicType(self, address): + dynTypeName = self.dynamicTypeName(address) + if dynTypeName is not None: + return self.dumper.createType(dynTypeName) + return self + def check(self): - if self.name is None: - error('TYPE WITHOUT NAME') + tdata = self.typeData + if tdata is None: + error('TYPE WITHOUT DATA: %s ALL: %s' % (self.typeId, self.dumper.typeData.keys())) + if tdata.name is None: + error('TYPE WITHOUT NAME: %s' % self.typeId) def dereference(self): + if self.code == TypeCodeTypedef: + return self.ltarget.dereference() self.check() - if self.nativeType is not None: - return self.dumper.nativeTypeDereference(self.nativeType) - error('DONT KNOW HOW TO DEREF: %s' % self.name) + return self.ltarget def unqualified(self): - if self.nativeType is not None: - return self.dumper.nativeTypeUnqualified(self.nativeType) return self def templateArgument(self, position, numeric = False): - if self.templateArguments is not None: - return self.templateArguments[position] - nativeType = self.nativeType - #warn('NATIVE TYPE 0: %s' % dir(nativeType)) - if nativeType is None: - nativeType = self.dumper.lookupNativeType(self.name) - #warn('NATIVE TYPE 1: %s' % dir(nativeType)) - if nativeType is not None: - return self.dumper.nativeTypeTemplateArgument(nativeType, position, numeric) - res = self.dumper.extractTemplateArgument(self.name, position) - #warn('TEMPLATE ARG: RES: %s' % res) - if numeric: - return int(res) - return self.dumper.createType(res) + tdata = self.typeData + #warn('TDATA: %s' % tdata) + #warn('ID: %s' % self.typeId) + if tdata is None: + # Native lookups didn't help. Happens for 'wrong' placement of 'const' + # etc. with LLDB. But not all is lost: + ta = self.dumper.listTemplateParameters(self.typeId) + #warn('MANUAL: %s' % ta) + res = ta[position] + #warn('RES: %s' % res.typeId) + return res + #warn('TA: %s %s' % (position, self.typeId)) + #warn('ARGS: %s' % tdata.templateArguments) + res = tdata.templateArguments[position] + #if tdata.templateArguments is not None: + #if numeric: + # return tdata.templateArguments[position].value() + return res def simpleEncoding(self): res = { @@ -2999,10 +3174,11 @@ class DumperBase: return self.code in (TypeCodeIntegral, TypeCodeFloat, TypeCodeEnum) def alignment(self): + #warn('ALIGN: %s' % self.stringify()) if self.code == TypeCodeTypedef: - return self.stripTypedefs().alignment() + return self.ltarget.alignment() if self.isSimpleType(): - if self.name == 'double': + if self.name in ('double', 'long long', 'unsigned long long'): return self.dumper.ptrSize() # Crude approximation. return self.size() if self.code == TypeCodePointer: @@ -3010,6 +3186,7 @@ class DumperBase: fields = self.fields() align = 1 for field in fields: + #warn(' SUBFIELD: %s TYPE %s' % (field.name, field.fieldType().name)) a = field.fieldType().alignment() #warn(' SUBFIELD: %s ALIGN: %s' % (field.name, a)) if a is not None and a > align: @@ -3018,9 +3195,7 @@ class DumperBase: return align def pointer(self): - if self.nativeType is not None: - return self.dumper.nativeTypePointer(self.nativeType) - error('Cannot create pointer type for %s' % self) + return self.dumper.createPointerType(self) def splitArrayType(self): # -> (inner type, count) @@ -3033,44 +3208,17 @@ class DumperBase: return (self.dumper.createType(s[0:pos1].strip()), int(s[pos1+1:pos2])) def target(self): - if self.nativeType is not None: - target = self.dumper.nativeTypeTarget(self.nativeType) - #warn('DEREFERENCING: %s -> %s ' % (self.nativeType, target)) - if target is not None: - return target - if self.code == TypeCodeArray: - (innerType, itemCount) = self.splitArrayType() - #warn('EXTRACTING ARRAY TYPE: %s -> %s' % (self, innerType)) - # HACK for LLDB 320: - if innerType.code is None and innerType.name.endswith(']'): - innerType.code = TypeCodeArray - return innerType - - strippedType = self.stripTypedefs() - if strippedType.name != self.name: - return strippedType.target() - error('DONT KNOW TARGET FOR: %s' % self) + return self.typeData.ltarget def fields(self): - #warn('GETTING FIELDS FOR: %s' % self.name) - if self.lfields is not None: - warn('USING LFIELDS: %s' % self.lfields) - return self.lfields - nativeType = self.nativeType - if nativeType is None: - nativeType = self.dumper.lookupNativeType(self.name) - #warn('FIELDS LOOKING UP NATIVE TYPE FOR %s -> %s' % (self.name, nativeType)) - if nativeType is not None: - #warn('FIELDS USING NATIVE TYPE %s' % nativeType) - fields = self.dumper.nativeTypeFields(nativeType) - #warn('FIELDS RES: %s FOR %s' % (fields, nativeType)) - return fields - error('DONT KNOW FIELDS FOR: %s' % self.stringify()) - return [] + if self.code == TypeCodeTypedef: + return self.ltarget.fields() + return self.lfields def firstBase(self): - if self.nativeType is not None: - return self.dumper.nativeTypeFirstBase(self.nativeType) + lfields = self.fields() + if len(lfields) > 0 and lfields[0].isBaseClass: + return lfields[0].ltype return None def field(self, name, bitoffset = 0): @@ -3094,14 +3242,10 @@ class DumperBase: return None def stripTypedefs(self): - if self.code != TypeCodeTypedef: + if isinstance(self, self.dumper.Type) and self.code != TypeCodeTypedef: #warn('NO TYPEDEF: %s' % self) return self - if self.nativeType is not None: - res = self.dumper.nativeTypeStripTypedefs(self.nativeType) - #warn('STRIP TYPEDEF: %s -> %s' % (self, res)) - return res - error('DONT KNOW HOW TO STRIP TYPEDEFS FROM %s' % s) + return self.ltarget def size(self): bs = self.bitsize() @@ -3136,11 +3280,6 @@ class DumperBase: return True return strippedName == 'QStringList' and self.dumper.qtVersion() >= 0x050000 - def enumDisplay(self, intval): - if self.nativeType is not None: - return self.dumper.nativeTypeEnumDisplay(self.nativeType, intval) - return '%d' % intval - class Field: def __init__(self, dumper): self.dumper = dumper @@ -3159,8 +3298,8 @@ class DumperBase: return ('Field(name="%s",ltype=%s,parentType=%s,bpos=%s,bsize=%s,' + 'bidx=%s,nidx=%s)') \ % (self.name, - None if self.ltype is None else self.ltype.name, - None if self.parentType is None else self.parentType.name, + self.ltype.stringify(), + self.parentType.typeId, self.lbitpos, self.lbitsize, self.baseIndex, self.nativeIndex) @@ -3207,11 +3346,122 @@ class DumperBase: #error('CANT GET FIELD TYPE FOR %s' % self) return None + def toPointerData(self, address): + if not self.isInt(address): + error('wrong') + size = self.ptrSize() + code = 'I' if size == 4 else 'Q' + return bytes(struct.pack(code, address)) + + def createPointerValue(self, targetAddress, targetType): + if not isinstance(targetType, self.Type): + error('Expected type in createPointerValue(), got %s' + % type(targetType)) + if not self.isInt(targetAddress): + error('Expected integral address value in createPointerValue(), got %s' + % type(targetType)) + val = self.Value(self) + val.ldata = self.toPointerData(targetAddress) + targetType = targetType.dynamicType(targetAddress) + val.type = self.createPointerType(targetType) + return val + + def createReferenceValue(self, targetAddress, targetType): + if not isinstance(targetType, self.Type): + error('Expected type in createReferenceValue(), got %s' + % type(targetType)) + if not self.isInt(targetAddress): + error('Expected integral address value in createReferenceValue(), got %s' + % type(targetType)) + val = self.Value(self) + val.ldata = self.toPointerData(targetAddress) + targetType = targetType.dynamicType(targetAddress) + val.type = self.createReferenceType(targetType) + return val + + def createPointerType(self, targetType): + if not isinstance(targetType, self.Type): + error('Expected type in createPointerType(), got %s' + % type(targetType)) + typeId = targetType.typeId + ' *' + tdata = self.TypeData(self) + tdata.name = targetType.name + '*' + tdata.typeId = typeId + tdata.lbitsize = 8 * self.ptrSize() + tdata.code = TypeCodePointer + tdata.ltarget = targetType + tdata.lfields = [] + self.registerType(typeId, tdata) + return self.Type(self, typeId) + + def createReferenceType(self, targetType): + if not isinstance(targetType, self.Type): + error('Expected type in createReferenceType(), got %s' + % type(targetType)) + typeId = targetType.typeId + ' &' + tdata = self.TypeData(self) + tdata.name = targetType.name + ' &' + tdata.typeId = typeId + tdata.code = TypeCodeReference + tdata.ltarget = targetType + tdata.lbitsize = 8 * self.ptrSize() # Needed for Gdb13393 test. + #tdata.lbitsize = None + self.registerType(typeId, tdata) + return self.Type(self, typeId) + + def createArrayType(self, targetType, count): + if not isinstance(targetType, self.Type): + error('Expected type in createArrayType(), got %s' + % type(targetType)) + typeId = '%s[%d]' % (targetType.typeId, count) + tdata = self.TypeData(self) + tdata.name = '%s[%d]' % (targetType.name, count) + tdata.typeId = typeId + tdata.code = TypeCodeArray + tdata.ltarget = targetType + tdata.lbitsize = count * targetType.lbitsize + fields = [] + for i in range(count): + field = self.Field(self) + field.ltype = targetType + field.parentType = None + field.isBaseClass = False + field.lbitsize = targetType.lbitsize + field.lbitpos = i * targetType.lbitsize * 8 + fields.append(field) + #tdata.lfields = fields + self.registerType(typeId, tdata) + return self.Type(self, typeId) + + def createTypedefedType(self, targetType, typeId): + if not isinstance(targetType, self.Type): + error('Expected type in createTypedefType(), got %s' + % type(targetType)) + # Happens for C-style struct in GDB: typedef { int x; } struct S1; + if targetType.typeId == typeId: + return targetType + tdata = self.TypeData(self) + tdata.name = typeId + tdata.typeId = typeId + tdata.code = TypeCodeTypedef + tdata.ltarget = targetType + tdata.lbitsize = targetType.lbitsize + tdata.lfields = targetType.lfields + self.registerType(typeId, tdata) + return self.Type(self, typeId) + def createType(self, typish, size = None): if isinstance(typish, self.Type): - typish.check() + #typish.check() return typish if isinstance(typish, str): + if typish.endswith(']') and not typish.endswith('[]'): + # Array fallback. + pos1 = typish.rfind('[') + itemType = self.createType(typish[0:pos1].strip()) + itemCount = int(typish[pos1+1:-1]) + return self.createArrayType(itemType, itemCount) + def knownSize(tn): if tn[0] == 'Q': if tn in ('QByteArray', 'QString', 'QList', 'QStringList', @@ -3234,26 +3484,35 @@ class DumperBase: ns = self.qtNamespace() typish = typish.replace('@', ns) if typish.startswith(ns): - size = knownSize(typish[len(ns):]) + if size is None: + size = knownSize(typish[len(ns):]) else: - size = knownSize(typish) + if size is None: + size = knownSize(typish) if size is not None: typish = ns + typish - #typeobj = self.Type(self) - #typeobj.name = typish - nativeType = self.lookupNativeType(typish) # FIXME: Remove? - #warn('FOUND NAT TYPE: %s' % dir(nativeType)) - if nativeType is not None: + + tdata = self.typeData.get(typish, None) + if tdata is not None: + return self.Type(self, typish) + + knownType = self.lookupType(typish) + #warn('KNOWN: %s' % knownType) + if knownType is not None: #warn('USE FROM NATIVE') - typeobj = self.fromNativeType(nativeType) - else: - #warn('FAKING') - typeobj = self.Type(self) - typeobj.name = typish - if size is not None: - typeobj.lbitsize = 8 * size - #warn('CREATE TYPE: %s' % typeobj) + return knownType + + #warn('FAKING: %s SIZE: %s' % (typish, size)) + tdata = self.TypeData(self) + tdata.name = typish + tdata.typeId = typish + + if size is not None: + tdata.lbitsize = 8 * size + self.registerType(typish, tdata) + typeobj = self.Type(self, typish) + #warn('CREATE TYPE: %s' % typeobj.stringify()) typeobj.check() return typeobj error('NEED TYPE, NOT %s' % type(typish)) @@ -3262,38 +3521,37 @@ class DumperBase: val = self.Value(self) val.type = self.createType(typish) if self.isInt(datish): # Used as address. + #warn('CREATING %s AT 0x%x' % (val.type.name, datish)) val.laddress = datish - #warn('CREATING %s AT 0x%x' % (val.type.name, address)) - elif isinstance(datish, bytes): - val.ldata = datish - val.type.lbitsize = 8 * len(datish) + val.type = val.type.dynamicType(datish) + return val + if isinstance(datish, bytes): #warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish))) - else: - error('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish)) - val.check() + val.ldata = datish + val.check() + return val + error('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish)) + + def createContainerItem(self, data, innerTypish, container): + innerType = self.createType(innerTypish) + name = self.qtNamespace() + '%s<%s>' % (container, innerType.name) + typeId = name + tdata = self.TypeData(self) + tdata.typeId = typeId + tdata.name = name + tdata.templateArguments = [innerType] + tdata.lbitsize = 8 * self.ptrSize() + self.registerType(typeId, tdata) + val = self.Value(self) + val.ldata = data + val.type = self.Type(self, typeId) return val def createListItem(self, data, innerTypish): - innerType = self.createType(innerTypish) - typeobj = self.Type(self) - typeobj.name = self.qtNamespace() + 'QList<%s>' % innerType.name - typeobj.templateArguments = [innerType] - typeobj.lbitsize = 8 * self.ptrSize() - val = self.Value(self) - val.ldata = data - val.type = typeobj - return val + return self.createContainerItem(data, innerTypish, 'QList') def createVectorItem(self, data, innerTypish): - innerType = self.createType(innerTypish) - typeobj = self.Type(self) - typeobj.name = self.qtNamespace() + 'QVector<%s>' % innerType.name - typeobj.templateArguments = [innerType] - typeobj.lbitsize = 8 * self.ptrSize() - val = self.Value(self) - val.ldata = data - val.type = typeobj - return val + return self.createContainerItem(data, innerTypish, 'QVector') class StructBuilder: def __init__(self, dumper): diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index 9ab49555f51..b1db09ad15d 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -205,17 +205,46 @@ class Dumper(DumperBase): self.output = [] self.setVariableFetchingOptions(args) - def fromNativeDowncastableValue(self, nativeValue): + def fromFrameValue(self, nativeValue): + val = nativeValue if self.useDynamicType: try: - return self.fromNativeValue(nativeValue.cast(nativeValue.dynamic_type)) + val = nativeValue.cast(nativeValue.dynamic_type) except: pass - return self.fromNativeValue(nativeValue) + return self.fromNativeValue(val) def fromNativeValue(self, nativeValue): - #self.check(isinstance(nativeValue, gdb.Value)) + #warn("FROM NATIVE VALUE: %s" % nativeValue) + self.check(isinstance(nativeValue, gdb.Value)) nativeType = nativeValue.type + code = nativeType.code + if code == gdb.TYPE_CODE_REF: + targetType = self.fromNativeType(nativeType.target().unqualified()) + val = self.createReferenceValue(toInteger(nativeValue.address), targetType) + #warn("CREATED REF: %s" % val) + return val + if code == gdb.TYPE_CODE_PTR: + targetType = self.fromNativeType(nativeType.target().unqualified()) + val = self.createPointerValue(toInteger(nativeValue), targetType) + #warn("CREATED PTR 1: %s" % val) + if not nativeValue.address is None: + val.laddress = toInteger(nativeValue.address) + #warn("CREATED PTR 2: %s" % val) + return val + if code == gdb.TYPE_CODE_TYPEDEF: + targetType = nativeType.strip_typedefs().unqualified() + #warn("TARGET TYPE: %s" % targetType) + if targetType.code == gdb.TYPE_CODE_ARRAY: + val = self.Value(self) + val.laddress = toInteger(nativeValue.address) + else: + # Cast may fail (e.g for arrays, see test for Bug5799) + val = self.fromNativeValue(nativeValue.cast(targetType)) + val.type = self.fromNativeType(nativeType) + #warn("CREATED TYPEDEF: %s" % val) + return val + val = self.Value(self) if not nativeValue.address is None: val.laddress = toInteger(nativeValue.address) @@ -238,82 +267,131 @@ class Dumper(DumperBase): val.ldisplay += ' (%s)' % intval elif code == gdb.TYPE_CODE_COMPLEX: val.ldisplay = str(nativeValue) - elif code == gdb.TYPE_CODE_ARRAY: - val.type.ltarget = nativeValue[0].type.unqualified() + #elif code == gdb.TYPE_CODE_ARRAY: + # val.type.ltarget = nativeValue[0].type.unqualified() return val + def ptrSize(self): + result = gdb.lookup_type('void').pointer().sizeof + self.ptrSize = lambda: result + return result + def fromNativeType(self, nativeType): self.check(isinstance(nativeType, gdb.Type)) - typeobj = self.Type(self) - typeobj.nativeType = nativeType.unqualified() - typeobj.name = str(typeobj.nativeType) - typeobj.lbitsize = nativeType.sizeof * 8 - typeobj.code = { - gdb.TYPE_CODE_TYPEDEF : TypeCodeTypedef, - gdb.TYPE_CODE_METHOD : TypeCodeFunction, - gdb.TYPE_CODE_VOID : TypeCodeVoid, - gdb.TYPE_CODE_FUNC : TypeCodeFunction, - gdb.TYPE_CODE_METHODPTR : TypeCodeFunction, - gdb.TYPE_CODE_MEMBERPTR : TypeCodeFunction, - gdb.TYPE_CODE_PTR : TypeCodePointer, - gdb.TYPE_CODE_REF : TypeCodeReference, - gdb.TYPE_CODE_BOOL : TypeCodeIntegral, - gdb.TYPE_CODE_CHAR : TypeCodeIntegral, - gdb.TYPE_CODE_INT : TypeCodeIntegral, - gdb.TYPE_CODE_FLT : TypeCodeFloat, - gdb.TYPE_CODE_ENUM : TypeCodeEnum, - gdb.TYPE_CODE_ARRAY : TypeCodeArray, - gdb.TYPE_CODE_STRUCT : TypeCodeStruct, - gdb.TYPE_CODE_UNION : TypeCodeStruct, - gdb.TYPE_CODE_COMPLEX : TypeCodeComplex, - gdb.TYPE_CODE_STRING : TypeCodeFortranString, - }[nativeType.code] - return typeobj + code = nativeType.code + #warn('FROM NATIVE TYPE: %s' % nativeType) + #nativeType = nativeType.unqualified() - def nativeTypeDereference(self, nativeType): - return self.fromNativeType(nativeType.strip_typedefs().target()) + if code == gdb.TYPE_CODE_PTR: + #warn('PTR') + targetType = self.fromNativeType(nativeType.target().unqualified()) + return self.createPointerType(targetType) - def nativeTypeUnqualified(self, nativeType): - return self.fromNativeType(nativeType.unqualified()) + if code == gdb.TYPE_CODE_REF: + #warn('REF') + targetType = self.fromNativeType(nativeType.target().unqualified()) + return self.createReferenceType(targetType) - def nativeTypePointer(self, nativeType): - return self.fromNativeType(nativeType.pointer()) + if code == gdb.TYPE_CODE_ARRAY: + #warn('ARRAY') + nativeTargetType = nativeType.target().unqualified() + targetType = self.fromNativeType(nativeTargetType) + count = nativeType.sizeof // nativeTargetType.sizeof + return self.createArrayType(targetType, count) - def nativeTypeTarget(self, nativeType): - while nativeType.code == gdb.TYPE_CODE_TYPEDEF: - nativeType = nativeType.strip_typedefs().unqualified() - return self.fromNativeType(nativeType.target()) + if code == gdb.TYPE_CODE_TYPEDEF: + #warn('TYPEDEF') + nativeTargetType = nativeType.unqualified() + while nativeTargetType.code == gdb.TYPE_CODE_TYPEDEF: + nativeTargetType = nativeTargetType.strip_typedefs().unqualified() + targetType = self.fromNativeType(nativeTargetType) + return self.createTypedefedType(targetType, str(nativeType)) - def nativeTypeFirstBase(self, nativeType): - nativeFields = nativeType.fields() - if len(nativeFields) and nativeFields[0].is_base_class: - return self.fromNativeType(nativeFields[0].type) + if code == gdb.TYPE_CODE_ERROR: + warn('Type error: %s' % nativeType) + return self.Type(self, '') + + typeId = self.nativeTypeId(nativeType) + res = self.typeData.get(typeId, None) + if res is None: + tdata = self.TypeData(self) + tdata.name = str(nativeType) + tdata.typeId = typeId + tdata.lbitsize = nativeType.sizeof * 8 + tdata.code = { + #gdb.TYPE_CODE_TYPEDEF : TypeCodeTypedef, # Handled above. + gdb.TYPE_CODE_METHOD : TypeCodeFunction, + gdb.TYPE_CODE_VOID : TypeCodeVoid, + gdb.TYPE_CODE_FUNC : TypeCodeFunction, + gdb.TYPE_CODE_METHODPTR : TypeCodeFunction, + gdb.TYPE_CODE_MEMBERPTR : TypeCodeFunction, + #gdb.TYPE_CODE_PTR : TypeCodePointer, # Handled above. + #gdb.TYPE_CODE_REF : TypeCodeReference, # Handled above. + gdb.TYPE_CODE_BOOL : TypeCodeIntegral, + gdb.TYPE_CODE_CHAR : TypeCodeIntegral, + gdb.TYPE_CODE_INT : TypeCodeIntegral, + gdb.TYPE_CODE_FLT : TypeCodeFloat, + gdb.TYPE_CODE_ENUM : TypeCodeEnum, + #gdb.TYPE_CODE_ARRAY : TypeCodeArray, + gdb.TYPE_CODE_STRUCT : TypeCodeStruct, + gdb.TYPE_CODE_UNION : TypeCodeStruct, + gdb.TYPE_CODE_COMPLEX : TypeCodeComplex, + gdb.TYPE_CODE_STRING : TypeCodeFortranString, + }[code] + if tdata.code == TypeCodeEnum: + tdata.enumDisplay = lambda intval: self.nativeTypeEnumDisplay(nativeType, intval) + self.registerType(typeId, tdata) # Prevent recursion in fields. + tdata.lfields = self.listFields(nativeType, self.Type(self, typeId)) + tdata.templateArguments = self.listTemplateParameters(nativeType) + self.registerType(typeId, tdata) # Fix up fields and template args + # warn('CREATE TYPE: %s' % typeId) + #else: + # warn('REUSE TYPE: %s' % typeId) + return self.Type(self, typeId) + + def listTemplateParameters(self, nativeType): + targs = [] + pos = 0 + while True: + try: + targ = nativeType.template_argument(pos) + except: + break + if isinstance(targ, gdb.Type): + targs.append(self.fromNativeType(targ.unqualified())) + elif isinstance(targ, gdb.Value): + #targs.append(self.fromNativeValue(targ)) + targs.append(self.fromNativeValue(targ).value()) + else: + error('CRAP') + pos += 1 + return targs def nativeTypeEnumDisplay(self, nativeType, intval): try: - val = gdb.parse_and_eval("(%s)%d" % (nativeType, intval)) - return "%s (%d)" % (val, intval) + val = gdb.parse_and_eval('(%s)%d' % (nativeType, intval)) + return '%s (%d)' % (val, intval) except: - return "%d" % intval + return '%d' % intval - def nativeTypeFields(self, nativeType): - if nativeType.code == gdb.TYPE_CODE_TYPEDEF: - return self.nativeTypeFields(nativeType.strip_typedefs()) + def nativeTypeId(self, nativeType): + name = str(nativeType) + if len(name) == 0: + c = '0' + elif name == 'struct {...}': + c = 's' + elif name == 'union {...}': + c = 'u' + else: + return name + typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.type)) for f in nativeType.fields()]) + return typeId + + def listFields(self, nativeType, parentType): + #if nativeType.code == gdb.TYPE_CODE_TYPEDEF: + # return self.listFields(nativeType.strip_typedefs(), parentType) fields = [] - if nativeType.code == gdb.TYPE_CODE_ARRAY: - # An array. - typeobj = nativeType.strip_typedefs() - innerType = typeobj.target() - for i in xrange(int(typeobj.sizeof / innerType.sizeof)): - field = self.Field(self) - field.ltype = self.fromNativeType(innerType) - field.parentType = self.fromNativeType(nativeType) - field.isBaseClass = False - field.lbitsize = innerType.sizeof - field.lbitpos = i * innerType.sizeof * 8 - fields.append(field) - return fields if not nativeType.code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION): return fields @@ -321,18 +399,20 @@ class Dumper(DumperBase): nativeIndex = 0 baseIndex = 0 nativeFields = nativeType.fields() - #warn("NATIVE FIELDS: %s" % nativeFields) + #warn('NATIVE FIELDS: %s' % nativeFields) + anonNumber = 0 for nativeField in nativeFields: - #warn("FIELD: %s" % nativeField) - #warn(" DIR: %s" % dir(nativeField)) - #warn(" BITSIZE: %s" % nativeField.bitsize) - #warn(" ARTIFICIAL: %s" % nativeField.artificial) - #warn("FIELD NAME: %s" % nativeField.name) - #warn("FIELD TYPE: %s" % nativeField.type) + #warn('FIELD: %s' % nativeField) + #warn(' DIR: %s' % dir(nativeField)) + #warn(' BITSIZE: %s' % nativeField.bitsize) + #warn(' ARTIFICIAL: %s' % nativeField.artificial) + #warn('FIELD NAME: %s' % nativeField.name) + #warn('FIELD TYPE: %s' % nativeField.type) + #warn('FIELD TYPE ID: %s' % self.nativeTypeId(nativeField.type)) #self.check(isinstance(nativeField, gdb.Field)) field = self.Field(self) - field.ltype = self.fromNativeType(nativeField.type) - field.parentType = self.fromNativeType(nativeType) + field.ltype = self.fromNativeType(nativeField.type.unqualified()) + field.parentType = parentType field.name = nativeField.name field.isBaseClass = nativeField.is_base_class if hasattr(nativeField, 'bitpos'): @@ -356,36 +436,31 @@ class Dumper(DumperBase): # Something without a name. # Anonymous union? We need a dummy name to distinguish # multiple anonymous unions in the struct. - self.anonNumber += 1 - field.name = "#%s" % self.anonNumber + anonNumber += 1 + field.name = '#%s' % anonNumber else: # Normal named field. field.name = nativeField.name field.nativeIndex = nativeIndex + #warn('FIELD RESULT: %s' % field) fields.append(field) nativeIndex += 1 - #warn("FIELDS: %s" % fields) + #warn('FIELDS: %s' % fields) return fields - def nativeTypeStripTypedefs(self, typeobj): - typeobj = typeobj.unqualified() - while typeobj.code == gdb.TYPE_CODE_TYPEDEF: - typeobj = typeobj.strip_typedefs().unqualified() - return self.fromNativeType(typeobj) - def listOfLocals(self, partialVar): frame = gdb.selected_frame() try: block = frame.block() - #warn("BLOCK: %s " % block) + #warn('BLOCK: %s ' % block) except RuntimeError as error: - #warn("BLOCK IN FRAME NOT ACCESSIBLE: %s" % error) + #warn('BLOCK IN FRAME NOT ACCESSIBLE: %s' % error) return [] except: - warn("BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS") + warn('BLOCK NOT ACCESSIBLE FOR UNKNOWN REASONS') return [] items = [] @@ -409,10 +484,18 @@ class Dumper(DumperBase): # "NotImplementedError: Symbol type not yet supported in # Python scripts." #warn("SYMBOL %s (%s, %s)): " % (symbol, name, symbol.name)) - try: - value = self.fromNativeDowncastableValue(frame.read_var(name, block)) - #warn("READ 1: %s" % value) + if False and self.passExceptions: + value = self.fromFrameValue(frame.read_var(name, block)) value.name = name + #warn("READ 1: %s" % value.stringify()) + items.append(value) + continue + + try: + # Same as above, but for production. + value = self.fromFrameValue(frame.read_var(name, block)) + value.name = name + #warn("READ 1: %s" % value.stringify()) items.append(value) continue except: @@ -420,7 +503,7 @@ class Dumper(DumperBase): try: #warn("READ 2: %s" % item.value) - value = self.fromNativeDowncastableValue(frame.read_var(name)) + value = self.fromFrameValue(frame.read_var(name)) value.name = name items.append(value) continue @@ -435,7 +518,8 @@ class Dumper(DumperBase): try: #warn("READ 3: %s %s" % (name, item.value)) #warn("ITEM 3: %s" % item.value) - value = self.fromNativeDowncastableValue(gdb.parse_and_eval(name)) + value = self.fromFrameValue(gdb.parse_and_eval(name)) + value.name = name items.append(value) except: # Can happen in inlined code (see last line of @@ -550,10 +634,11 @@ class Dumper(DumperBase): exp = "((%s*)0x%x)->%s(%s)" % (typeName, addr, function, arg) #warn("CALL: %s" % exp) result = gdb.parse_and_eval(exp) - #warn(" -> %s" % result) + warn(" -> %s" % result) + res = self.fromNativeValue(result) if not value.address(): gdb.parse_and_eval("free((void*)0x%x)" % addr) - return self.fromNativeValue(result) + return res def makeExpression(self, value): typename = "::" + value.type.name @@ -573,13 +658,6 @@ class Dumper(DumperBase): #warn(" VALUE: %s" % value) return value - def nativeTypeTemplateArgument(self, nativeType, position, numeric): - #warn("NATIVE TYPE: %s" % dir(nativeType)) - arg = nativeType.template_argument(position) - if numeric: - return int(str(arg)) - return self.fromNativeType(arg) - def pokeValue(self, value): # Allocates inferior memory and copies the contents of value. # Returns a pointer to the copy. @@ -885,9 +963,6 @@ class Dumper(DumperBase): def enumExpression(self, enumType, enumValue): return self.qtNamespace() + "Qt::" + enumValue - def lookupType(self, typeName): - return self.fromNativeType(self.lookupNativeType(typeName)) - def lookupNativeType(self, typeName): nativeType = self.lookupNativeTypeHelper(typeName) if not nativeType is None: @@ -1154,7 +1229,7 @@ class CliDumper(Dumper): def putNumChild(self, numchild): pass - def putOriginalAddress(self, value): + def putOriginalAddress(self, address): pass def fetchVariables(self, args): @@ -1205,4 +1280,4 @@ class InterpreterMessageBreakpoint(gdb.Breakpoint): print("Interpreter event received.") return theDumper.handleInterpreterMessage() -InterpreterMessageBreakpoint() +#InterpreterMessageBreakpoint() diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index a1b336e966f..24b3d6ada15 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -44,7 +44,7 @@ from dumper import * qqWatchpointOffset = 10000 def showException(msg, exType, exValue, exTraceback): - warn("**** CAUGHT EXCEPTION: %s ****" % msg) + warn('**** CAUGHT EXCEPTION: %s ****' % msg) import traceback lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)] warn('\n'.join(lines)) @@ -55,13 +55,16 @@ def fileNameAsString(file): def check(exp): if not exp: - raise RuntimeError("Check failed") + raise RuntimeError('Check failed') class Dumper(DumperBase): def __init__(self): DumperBase.__init__(self) lldb.theDumper = self + self.isLldb = True + self.typeCache = {} + self.outputLock = threading.Lock() self.debugger = lldb.SBDebugger.Create() #self.debugger.SetLoggingCallback(loggingCallback) @@ -69,18 +72,18 @@ class Dumper(DumperBase): # s = args.strip() # s = s.replace('"', "'") # sys.stdout.write('log="%s"@\n' % s) - #Same as: self.debugger.HandleCommand("log enable lldb dyld step") - #self.debugger.EnableLog("lldb", ["dyld", "step", "process", "state", - # "thread", "events", - # "communication", "unwind", "commands"]) - #self.debugger.EnableLog("lldb", ["all"]) + #Same as: self.debugger.HandleCommand('log enable lldb dyld step') + #self.debugger.EnableLog('lldb', ['dyld', 'step', 'process', 'state', + # 'thread', 'events', + # 'communication', 'unwind', 'commands']) + #self.debugger.EnableLog('lldb', ['all']) self.debugger.Initialize() - self.debugger.HandleCommand("settings set auto-confirm on") + self.debugger.HandleCommand('settings set auto-confirm on') - # FIXME: warn("DISABLING DEFAULT FORMATTERS") + # FIXME: warn('DISABLING DEFAULT FORMATTERS') # It doesn't work at all with 179.5 and we have some bad # interaction in 300 - # if not hasattr(lldb.SBType, 'GetCanonicalType'): # "Test" for 179.5 + # if not hasattr(lldb.SBType, 'GetCanonicalType'): # 'Test' for 179.5 #self.debugger.HandleCommand('type category delete gnu-libstdc++') #self.debugger.HandleCommand('type category delete libcxx') #self.debugger.HandleCommand('type category delete default') @@ -109,24 +112,67 @@ class Dumper(DumperBase): self.interpreterBreakpointResolvers = [] self.report('lldbversion=\"%s\"' % lldb.SBDebugger.GetVersionString()) - self.reportState("enginesetupok") + self.reportState('enginesetupok') self.debuggerCommandInProgress = False + def fromNativeFrameValue(self, nativeValue): + return self.fromNativeValue(nativeValue) + def fromNativeValue(self, nativeValue): + self.check(isinstance(nativeValue, lldb.SBValue)) nativeValue.SetPreferSyntheticValue(False) nativeType = nativeValue.GetType() + code = nativeType.GetTypeClass() + if code == lldb.eTypeClassReference: + nativeTargetType = nativeType.GetDereferencedType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + targetType = self.fromNativeType(nativeTargetType) + val = self.createReferenceValue(nativeValue.GetValueAsUnsigned(), targetType) + val.laddress = nativeValue.AddressOf().GetValueAsUnsigned() + #warn('CREATED REF: %s' % val) + return val + if code == lldb.eTypeClassPointer: + nativeTargetType = nativeType.GetPointeeType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + targetType = self.fromNativeType(nativeTargetType) + val = self.createPointerValue(nativeValue.GetValueAsUnsigned(), targetType) + #warn('CREATED PTR 1: %s' % val) + val.laddress = nativeValue.AddressOf().GetValueAsUnsigned() + #warn('CREATED PTR 2: %s' % val) + return val + if code == lldb.eTypeClassTypedef: + nativeTargetType = nativeType.GetUnqualifiedType() + if hasattr(nativeTargetType, 'GetCanonicalType'): + nativeTargetType = nativeTargetType.GetCanonicalType() + val = self.fromNativeValue(nativeValue.Cast(nativeTargetType)) + val.type = self.fromNativeType(nativeType) + #warn('CREATED TYPEDEF: %s' % val) + return val + val = self.Value(self) + address = nativeValue.GetLoadAddress() + if not address is None: + val.laddress = address + if True: + #val.name = nativeValue.GetName() + data = nativeValue.GetData() + error = lldb.SBError() + size = nativeValue.GetType().GetByteSize() + if size > 1: + # 0 happens regularly e.g. for cross-shared-object types. + # 1 happens on Linux + try: + val.ldata = data.ReadRawData(error, 0, size) + except: + pass + val.type = self.fromNativeType(nativeType) val.lIsInScope = nativeValue.IsInScope() - #val.name = nativeValue.GetName() - data = nativeValue.GetData() - error = lldb.SBError() - size = nativeValue.GetType().GetByteSize() - if size > 0: # Happens regularly e.g. for cross-shared-object types. - val.ldata = data.ReadRawData(error, 0, size) - code = nativeType.GetTypeClass() - if code not in (lldb.eTypeClassPointer, lldb.eTypeClassReference): - val.laddress = int(nativeValue.GetLoadAddress()) + + #if code not in (lldb.eTypeClassPointer, lldb.eTypeClassReference): + # val.laddress = int(nativeValue.GetLoadAddress()) if code == lldb.eTypeClassEnumeration: intval = nativeValue.GetValueAsSigned() if hasattr(nativeType, 'get_enum_members_array'): @@ -137,69 +183,233 @@ class Dumper(DumperBase): if diff & mask == 0: path = nativeType.GetName().split('::') path[-1] = enumMember.GetName() - val.ldisplay = "%s (%d)" % ('::'.join(path), intval) - val.ldisplay = "%d" % intval + val.ldisplay = '%s (%d)' % ('::'.join(path), intval) + val.ldisplay = '%d' % intval elif code in (lldb.eTypeClassComplexInteger, lldb.eTypeClassComplexFloat): val.ldisplay = str(nativeValue.GetValue()) - elif code == lldb.eTypeClassArray: - if hasattr(nativeType, "GetArrayElementType"): # New in 3.8(?) / 350.x - val.type.ltarget = self.fromNativeType(nativeType.GetArrayElementType()) - else: - fields = nativeType.get_fields_array() - if len(fields): - val.type.ltarget = self.fromNativeType(fields[0]) - elif code == lldb.eTypeClassVector: - val.type.ltarget = self.fromNativeType(nativeType.GetVectorElementType()) + elif code == lldb.eTypeClassReference: + #val.laddress = int(nativeValue.GetLoadAddress()) + #val.ldata = None + derefNativeValue = nativeValue.Dereference() + derefNativeValue = derefNativeValue.Cast(derefNativeValue.GetType().GetUnqualifiedType()) + val1 = self.Value(self) + val1.type = val.type + val1.targetValue = self.fromNativeValue(derefNativeValue) + return val1 + #elif code == lldb.eTypeClassArray: + # if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x + # val.type.ltarget = self.fromNativeType(nativeType.GetArrayElementType()) + # else: + # fields = nativeType.get_fields_array() + # if len(fields): + # val.type.ltarget = self.fromNativeType(fields[0]) + #elif code == lldb.eTypeClassVector: + # val.type.ltarget = self.fromNativeType(nativeType.GetVectorElementType()) return val - def fromNativeType(self, nativeType): - typeobj = self.Type(self) - if nativeType.IsPointerType(): - typeobj.nativeType = nativeType - else: - # This strips typedefs for pointers. We don't want that. - typeobj.nativeType = nativeType.GetUnqualifiedType() - if hasattr(typeobj.nativeType, "GetDisplayTypeName"): - typeobj.name = typeobj.nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320) - else: - typeobj.name = typeobj.nativeType.GetName() # Xcode 5 (lldb-310) - typeobj.lbitsize = nativeType.GetByteSize() * 8 - code = nativeType.GetTypeClass() - try: - typeobj.code = { - lldb.eTypeClassArray : TypeCodeArray, - lldb.eTypeClassVector : TypeCodeArray, - lldb.eTypeClassComplexInteger : TypeCodeComplex, - lldb.eTypeClassComplexFloat : TypeCodeComplex, - lldb.eTypeClassClass : TypeCodeStruct, - lldb.eTypeClassStruct : TypeCodeStruct, - lldb.eTypeClassUnion : TypeCodeStruct, - lldb.eTypeClassEnumeration : TypeCodeEnum, - lldb.eTypeClassTypedef : TypeCodeTypedef, - lldb.eTypeClassReference : TypeCodeReference, - lldb.eTypeClassPointer : TypeCodePointer, - lldb.eTypeClassFunction : TypeCodeFunction, - lldb.eTypeClassMemberPointer : TypeCodeMemberPointer - }[code] - except KeyError: - if code == lldb.eTypeClassBuiltin: - if isFloatingPointTypeName(typeobj.name): - typeobj.code = TypeCodeFloat - elif isIntegralTypeName(typeobj.name): - typeobj.code = TypeCodeIntegral - else: - warn("UNKNOWN TYPE KEY: %s: %s" % (typeobj.name, code)) - else: - warn("UNKNOWN TYPE KEY: %s: %s" % (typeobj.name, code)) - #warn("CREATE TYPE: %s CODE: %s" % (typeobj.name, typeobj.code)) - return typeobj + def ptrSize(self): + result = self.target.GetAddressByteSize() + self.ptrSize = lambda: result + return result - def nativeTypeFields(self, nativeType): + def fromNativeType(self, nativeType): + self.check(isinstance(nativeType, lldb.SBType)) + code = nativeType.GetTypeClass() + + #warn('CURRENT: %s' % self.typeData.keys()) + #warn('FROM NATIVE TYPE: %s' % nativeType.GetName()) + if code == lldb.eTypeClassInvalid: + return None + + if code == lldb.eTypeClassBuiltin: + nativeType = nativeType.GetUnqualifiedType() + + if code == lldb.eTypeClassPointer: + #warn('PTR') + nativeTargetType = nativeType.GetPointeeType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + #warn('PTR: %s' % nativeTargetType.name) + return self.createPointerType(self.fromNativeType(nativeTargetType)) + + if code == lldb.eTypeClassReference: + #warn('REF') + nativeTargetType = nativeType.GetDereferencedType() + if not nativeTargetType.IsPointerType(): + nativeTargetType = nativeTargetType.GetUnqualifiedType() + #warn('REF: %s' % nativeTargetType.name) + return self.createReferenceType(self.fromNativeType(nativeTargetType)) + + if code in (lldb.eTypeClassArray, lldb.eTypeClassVector): + #warn('ARRAY: %s' % nativeType.GetName()) + if hasattr(nativeType, 'GetArrayElementType'): # New in 3.8(?) / 350.x + nativeTargetType = nativeType.GetArrayElementType() + if not nativeTargetType.IsValid(): + if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x + #warn('BAD: %s ' % nativeTargetType.get_fields_array()) + nativeTargetType = nativeType.GetVectorElementType() + elif hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x + nativeTargetType = nativeType.GetVectorElementType() + else: + return self.createType(nativeType.GetName()) + + #warn('NATIVE TARGET TYPE: %s' % nativeTargetType) + #warn('NATIVE TARGE NAME: %s' % nativeTargetType.GetName()) + targetType = self.fromNativeType(nativeTargetType) + count = nativeType.GetByteSize() // nativeTargetType.GetByteSize() + return self.createArrayType(targetType, count) + + if code == lldb.eTypeClassTypedef: + #warn('TYPEDEF') + nativeTargetType = nativeType.GetUnqualifiedType() + if hasattr(nativeTargetType, 'GetCanonicalType'): + nativeTargetType = nativeTargetType.GetCanonicalType() + targetType = self.fromNativeType(nativeTargetType) + return self.createTypedefedType(targetType, nativeType.GetName()) + + nativeType = nativeType.GetUnqualifiedType() + typeId = self.nativeTypeId(nativeType) + res = self.typeData.get(typeId, None) + if res is None: + # # This strips typedefs for pointers. We don't want that. + # typeobj.nativeType = nativeType.GetUnqualifiedType() + tdata = self.TypeData(self) + if hasattr(nativeType, 'GetDisplayTypeName'): + tdata.name = nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320) + else: + tdata.name = nativeType.GetName() # Xcode 5 (lldb-310) + tdata.typeId = typeId + tdata.lbitsize = nativeType.GetByteSize() * 8 + if code == lldb.eTypeClassBuiltin: + if isFloatingPointTypeName(tdata.name): + tdata.code = TypeCodeFloat + elif isIntegralTypeName(tdata.name): + tdata.code = TypeCodeIntegral + elif tdata.name == 'void': + tdata.code = TypeCodeVoid + else: + warn('UNKNOWN TYPE KEY: %s: %s' % (tdata.name, code)) + else: + # eTypeClassInvalid = (0u), + # eTypeClassArray = (1u << 0), + # eTypeClassBlockPointer = (1u << 1), + # eTypeClassBuiltin = (1u << 2), + # eTypeClassClass = (1u << 3), + # eTypeClassComplexFloat = (1u << 4), + # eTypeClassComplexInteger = (1u << 5), + # eTypeClassEnumeration = (1u << 6), + # eTypeClassFunction = (1u << 7), + # eTypeClassMemberPointer = (1u << 8), + # eTypeClassObjCObject = (1u << 9), + # eTypeClassObjCInterface = (1u << 10), + # eTypeClassObjCObjectPointer = (1u << 11), + # eTypeClassPointer = (1u << 12), + # eTypeClassReference = (1u << 13), + # eTypeClassStruct = (1u << 14), + # eTypeClassTypedef = (1u << 15), + # eTypeClassUnion = (1u << 16), + # eTypeClassVector = (1u << 17), + # // Define the last type class as the MSBit of a 32 bit value + # eTypeClassOther = (1u << 31), + # // Define a mask that can be used for any type when finding types + # eTypeClassAny = (0xffffffffu) + tdata.code = { + #lldb.eTypeClassArray : TypeCodeArray, + #lldb.eTypeClassVector : TypeCodeArray, + lldb.eTypeClassComplexInteger : TypeCodeComplex, + lldb.eTypeClassComplexFloat : TypeCodeComplex, + lldb.eTypeClassClass : TypeCodeStruct, + lldb.eTypeClassStruct : TypeCodeStruct, + lldb.eTypeClassUnion : TypeCodeStruct, + lldb.eTypeClassEnumeration : TypeCodeEnum, + #lldb.eTypeClassTypedef : TypeCodeTypedef, + #lldb.eTypeClassReference : TypeCodeReference, + #lldb.eTypeClassPointer : TypeCodePointer, + lldb.eTypeClassFunction : TypeCodeFunction, + lldb.eTypeClassMemberPointer : TypeCodeMemberPointer + }[code] + if tdata.code == TypeCodeEnum: + tdata.enumDisplay = lambda intval: self.nativeTypeEnumDisplay(nativeType, intval) + self.registerType(typeId, tdata) # Prevent recursion in fields. + tdata.lfields = self.listFields(nativeType, self.Type(self, typeId)) + tdata.templateArguments = self.listTemplateParametersHelper(nativeType) + self.registerType(typeId, tdata) # Fix up fields and template args + # warn('CREATE TYPE: %s' % typeId) + #else: + # warn('REUSE TYPE: %s' % typeId) + return self.Type(self, typeId) + + def listTemplateParametersHelper(self, nativeType): + stringArgs = self.listTemplateParameters(nativeType.GetName()) + n = nativeType.GetNumberOfTemplateArguments() + if n != len(stringArgs): + # Something wrong in the debug info. + # Should work in theory, doesn't work in practice. + # Items like std::allocator report 0 + # for nativeType.GetNumberOfTemplateArguments() with LLDB 3.8 + return stringArgs + + targs = [] + for i in range(nativeType.GetNumberOfTemplateArguments()): + kind = nativeType.GetTemplateArgumentKind(i) + # eTemplateArgumentKindNull = 0, + # eTemplateArgumentKindType, + # eTemplateArgumentKindDeclaration, + # eTemplateArgumentKindIntegral, + # eTemplateArgumentKindTemplate, + # eTemplateArgumentKindTemplateExpansion, + # eTemplateArgumentKindExpression, + # eTemplateArgumentKindPack + if kind == lldb.eTemplateArgumentKindType: + innerType = nativeType.GetTemplateArgumentType(i).GetUnqualifiedType().GetCanonicalType() + targs.append(self.fromNativeType(innerType)) + #elif kind == lldb.eTemplateArgumentKindIntegral: + # innerType = nativeType.GetTemplateArgumentType(i).GetUnqualifiedType().GetCanonicalType() + # #warn('INNER TYP: %s' % innerType) + # basicType = innerType.GetBasicType() + # #warn('IBASIC TYP: %s' % basicType) + # inner = self.extractTemplateArgument(nativeType.GetName(), i) + # exp = '(%s)%s' % (innerType.GetName(), inner) + # #warn('EXP : %s' % exp) + # val = self.nativeParseAndEvaluate('(%s)%s' % (innerType.GetName(), inner)) + # # Clang writes 'int' and '0xfffffff' into the debug info + # # LLDB manages to read a value of 0xfffffff... + # #if basicType == lldb.eBasicTypeInt: + # value = val.GetValueAsUnsigned() + # if value >= 0x8000000: + # value -= 0x100000000 + # #warn('KIND: %s' % kind) + # targs.append(value) + else: + #warn('UNHANDLED TEMPLATE TYPE : %s' % kind) + targs.append(stringArgs[i]) # Best we can do. + #warn('TARGS: %s %s' % (nativeType.GetName(), [str(x) for x in targs])) + return targs + + def nativeTypeId(self, nativeType): + if hasattr(nativeType, 'GetDisplayTypeName'): + name = nativeType.GetDisplayTypeName() # Xcode 6 (lldb-320) + else: + name = nativeType.GetName() # Xcode 5 (lldb-310) + if name is None or len(name) == 0: + c = '0' + elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassStruct: + c = 's' + elif name == '(anonymous struct)' and nativeType.GetTypeClass() == lldb.eTypeClassUnion: + c = 'u' + else: + return name + fields = nativeType.get_fields_array() + typeId = c + ''.join(['{%s:%s}' % (f.name, self.nativeTypeId(f.GetType())) for f in fields]) + #warn('NATIVE TYPE ID FOR %s IS %s' % (name, typeId)) + return typeId + + def listFields(self, nativeType, parentType): fields = [] if self.currentContextValue is not None: addr = self.currentContextValue.AddressOf().GetValueAsUnsigned() else: - warn("CREATING DUMMY CONTEXT") + warn('CREATING DUMMY CONTEXT') addr = 0 # FIXME: 0 doesn't produce valid member offsets. addr = 0x7fffffffe0a0 sbaddr = lldb.SBAddress(addr, self.target) @@ -226,9 +436,9 @@ class Dumper(DumperBase): f.GetOffsetInBits())) for f in nativeType.get_fields_array()) - #warn("BASE NAMES: %s" % baseNames) - #warn("VIRTUAL NAMES: %s" % virtualNames) - #warn("FIELD BITS: %s" % fieldBits) + #warn('BASE NAMES: %s' % baseNames) + #warn('VIRTUAL NAMES: %s' % virtualNames) + #warn('FIELD BITS: %s' % fieldBits) fieldParentType = self.fromNativeType(nativeType).stripTypedefs() # This does not list empty base entries. @@ -237,10 +447,10 @@ class Dumper(DumperBase): fieldName = dummyChild.GetName() if fieldName is None: anonNumber += 1 - fieldName = "#%s" % anonNumber - fieldType = dummyChild.GetType() - #warn("CHILD AT: %s: %s %s" % (i, fieldName, fieldType.GetName())) - #warn(" AT: %s: %s %s" % (i, fieldName, fieldType.GetName())) + fieldName = '#%s' % anonNumber + fieldType = dummyChild.GetType().GetUnqualifiedType() + #warn('CHILD AT: %s: %s %s' % (i, fieldName, fieldType.GetName())) + #warn(' AT: %s: %s %s' % (i, fieldName, fieldType.GetName())) caddr = dummyChild.AddressOf().GetValueAsUnsigned() child = self.Value(self) child.type = self.fromNativeType(fieldType) @@ -257,12 +467,12 @@ class Dumper(DumperBase): field.lbitsize = fieldType.GetByteSize() * 8 field.lbitpos = (caddr - addr) * 8 if fieldName in baseNames: - #warn("BASE: %s P0S: 0x%x - 0x%x = %s" % (fieldName, caddr, addr, caddr - addr)) + #warn('BASE: %s P0S: 0x%x - 0x%x = %s' % (fieldName, caddr, addr, caddr - addr)) field.isBaseClass = True field.baseIndex = baseNames[fieldName] if fieldName in virtualNames: field.isVirtualBase = True - #warn("ADDING VRITUAL BASE: %s" % fieldName) + #warn('ADDING VIRTUAL BASE: %s' % fieldName) fields.append(field) # Add empty bases. @@ -287,29 +497,10 @@ class Dumper(DumperBase): field.lbitpos = 0 fields.append(field) - #warn("FIELD NAMES: %s" % [field.name for field in fields]) - #warn("FIELDS: %s" % fields) + #warn('FIELD NAMES: %s' % [field.name for field in fields]) + #warn('FIELDS: %s' % fields) return fields - def nativeTypeUnqualified(self, nativeType): - return self.fromNativeType(nativeType.GetUnqualifiedType()) - - def nativeTypePointer(self, nativeType): - return self.fromNativeType(nativeType.GetPointerType()) - - def nativeTypeStripTypedefs(self, typeobj): - if hasattr(typeobj, 'GetCanonicalType'): - return self.fromNativeType(typeobj.GetCanonicalType()) - return self.fromNativeType(typeobj) - - def nativeTypeFirstBase(self, nativeType): - #warn("FIRST BASE FROM: %s" % nativeType) - if nativeType.GetNumberOfDirectBaseClasses() == 0: - return None - t = nativeType.GetDirectBaseClassAtIndex(0) - #warn(" GOT BASE FROM: %s" % t) - return self.fromNativeType(nativeType.GetDirectBaseClassAtIndex(0).GetType()) - def nativeTypeEnumDisplay(self, nativeType, intval): if hasattr(nativeType, 'get_enum_members_array'): for enumMember in nativeType.get_enum_members_array(): @@ -319,8 +510,8 @@ class Dumper(DumperBase): if diff & mask == 0: path = nativeType.GetName().split('::') path[-1] = enumMember.GetName() - return "%s (%d)" % ('::'.join(path), intval) - return "%d" % intval + return '%s (%d)' % ('::'.join(path), intval) + return '%d' % intval def nativeDynamicTypeName(self, address, baseType): return None # FIXME: Seems sufficient, no idea why. @@ -371,18 +562,18 @@ class Dumper(DumperBase): def enumExpression(self, enumType, enumValue): ns = self.qtNamespace() - return ns + "Qt::" + enumType + "(" \ - + ns + "Qt::" + enumType + "::" + enumValue + ")" + return ns + 'Qt::' + enumType + '(' \ + + ns + 'Qt::' + enumType + '::' + enumValue + ')' def callHelper(self, rettype, value, func, args): # args is a tuple. arg = ','.join(args) - #warn("PRECALL: %s -> %s(%s)" % (value.address(), func, arg)) + #warn('PRECALL: %s -> %s(%s)' % (value.address(), func, arg)) typename = value.type.name - exp = "((%s*)0x%x)->%s(%s)" % (typename, value.address(), func, arg) - #warn("CALL: %s" % exp) + exp = '((%s*)0x%x)->%s(%s)' % (typename, value.address(), func, arg) + #warn('CALL: %s' % exp) result = self.currentContextValue.CreateValueFromExpression('', exp) - #warn(" -> %s" % result) + #warn(' -> %s' % result) return self.fromNativeValue(result) def pokeValue(self, typeName, *args): @@ -390,12 +581,12 @@ class Dumper(DumperBase): frame = thread.GetFrameAtIndex(0) inner = ','.join(args) value = frame.EvaluateExpression(typeName + '{' + inner + '}') - #self.warn(" TYPE: %s" % value.type) - #self.warn(" ADDR: 0x%x" % value.address) - #self.warn(" VALUE: %s" % value) + #self.warn(' TYPE: %s' % value.type) + #self.warn(' ADDR: 0x%x' % value.address) + #self.warn(' VALUE: %s' % value) return value - def parseAndEvaluate(self, exp): + def nativeParseAndEvaluate(self, exp): thread = self.currentThread() frame = thread.GetFrameAtIndex(0) val = frame.EvaluateExpression(exp) @@ -403,51 +594,15 @@ class Dumper(DumperBase): #val = self.target.EvaluateExpression(exp, options) err = val.GetError() if err.Fail(): - #warn("FAILING TO EVAL: %s" % exp) + #warn('FAILING TO EVAL: %s' % exp) return None - #warn("NO ERROR.") - #warn("EVAL: %s -> %s" % (exp, val.IsValid())) - return self.fromNativeValue(val) + #warn('NO ERROR.') + #warn('EVAL: %s -> %s' % (exp, val.IsValid())) + return val - def nativeTypeTemplateArgument(self, nativeType, position, numeric = False): - if numeric: - # There seems no API to extract the numeric value. - inner = self.extractTemplateArgument(nativeType.GetName(), position) - innerType = nativeType.GetTemplateArgumentType(position) - basicType = innerType.GetBasicType() - value = toInteger(inner) - # Clang writes 'int' and '0xfffffff' into the debug info - # LLDB manages to read a value of 0xfffffff... - if basicType == lldb.eBasicTypeInt and value >= 0x8000000: - value -= 0x100000000 - return value - else: - #warn("nativeTypeTemplateArgument: %s: pos %s" % (nativeType, position)) - typeobj = nativeType.GetTemplateArgumentType(position).GetUnqualifiedType() - if typeobj.IsValid(): - # warn("TYPE: %s" % typeobj) - return self.fromNativeType(typeobj) - inner = self.extractTemplateArgument(nativeType.GetName(), position) - #warn("INNER: %s" % inner) - return self.lookupType(inner) - - def nativeTypeDereference(self, nativeType): - return self.fromNativeType(nativeType.GetPointeeType()) - - def nativeTypeTarget(self, nativeType): - code = nativeType.GetTypeClass() - if code == lldb.eTypeClassPointer: - return self.fromNativeType(nativeType.GetPointeeType()) - if code == lldb.eTypeClassReference: - return self.fromNativeType(nativeType.GetDereferencedType()) - if code == lldb.eTypeClassArray: - if hasattr(nativeType, "GetArrayElementType"): # New in 3.8(?) / 350.x - return self.fromNativeType(nativeType.GetArrayElementType()) - fields = nativeType.get_fields_array() - return None if not len(fields) else self.nativeTypeTarget(fields[0]) - if code == lldb.eTypeClassVector: - return self.fromNativeType(nativeType.GetVectorElementType()) - return self.fromNativeType(nativeType) + def parseAndEvaluate(self, exp): + val = self.nativeParseAndEvaluate(exp) + return None if val is None else self.fromNativeValue(val) def isWindowsTarget(self): return False @@ -524,41 +679,70 @@ class Dumper(DumperBase): return re.sub('\\bconst\\b', '', name).replace(' ', '') def lookupNativeType(self, name): - #warn("LOOKUP TYPE NAME: %s" % name) + #warn('LOOKUP TYPE NAME: %s' % name) + typeobj = self.typeCache.get(name) + if not typeobj is None: + #warn('CACHED: %s' % name) + return typeobj typeobj = self.target.FindFirstType(name) if typeobj.IsValid(): - #warn("VALID FIRST : %s" % dir(typeobj)) + #warn('VALID FIRST : %s' % typeobj) + self.typeCache[name] = typeobj return typeobj - typeobj = self.target.FindFirstType(name + '*') - if typeobj.IsValid(): - return typeob.GetPointeeType() - typeobj = self.target.FindFirstType(name + '&') - if typeobj.IsValid(): - return typeob.GetReferencedType() if name.endswith('*'): - typeobj = self.target.FindFirstType(name[:-1].strip()) - if typeobj.IsValid(): + #warn('RECURSE PTR') + typeobj = self.lookupNativeType(name[:-1].strip()) + if typeobj is not None: + #warn('RECURSE RESULT X: %s' % typeobj) + self.fromNativeType(typeobj.GetPointerType()) + #warn('RECURSE RESULT: %s' % typeobj.GetPointerType()) return typeobj.GetPointerType() - #warn("LOOKUP RESULT: %s" % typeobj.name) - #warn("LOOKUP VALID: %s" % typeobj.IsValid()) + + #typeobj = self.target.FindFirstType(name[:-1].strip()) + #if typeobj.IsValid(): + # self.typeCache[name] = typeobj.GetPointerType() + # return typeobj.GetPointerType() + + if name.endswith(' const'): + #warn('LOOKUP END CONST') + typeobj = self.lookupNativeType(name[:-6]) + if typeobj is not None: + return typeobj + + if name.startswith('const '): + #warn('LOOKUP START CONST') + typeobj = self.lookupNativeType(name[6:]) + if typeobj is not None: + return typeobj + needle = self.canonicalTypeName(name) - #self.warn("NEEDLE: %s " % needle) + #warn('NEEDLE: %s ' % needle) for i in xrange(self.target.GetNumModules()): module = self.target.GetModuleAtIndex(i) # SBModule.GetType is new somewhere after early 300.x # So this may fail. for t in module.GetTypes(): n = self.canonicalTypeName(t.GetName()) + #warn('N: %s' % n) if n == needle: - #self.warn("FOUND TYPE DIRECT 2: %s " % t) + #warn('FOUND TYPE DIRECT 2: %s ' % t) + self.typeCache[name] = t return t if n == needle + '*': - #self.warn("FOUND TYPE BY POINTER 2: %s " % t.GetPointeeType()) - return t.GetPointeeType() + res = t.GetPointeeType() + self.typeCache[name] = res + x = self.fromNativeType(res) # Register under both names + self.registerTypeAlias(x.typeId, name) + #warn('FOUND TYPE BY POINTER: %s ' % res.name) + return res if n == needle + '&': - #self.warn("FOUND TYPE BY REFERENCE 2: %s " % t) - return t.GetDereferencedType() - #warn("NOT FOUND: %s " % needle) + res = t.GetDereferencedType().GetUnqualifiedType() + self.typeCache[name] = res + x = self.fromNativeType(res) # Register under both names + self.registerTypeAlias(x.typeId, name) + #warn('FOUND TYPE BY REFERENCE: %s ' % res.name) + return res + #warn('NOT FOUND: %s ' % needle) return None def setupInferior(self, args): @@ -584,7 +768,7 @@ class Dumper(DumperBase): self.ignoreStops = 0 self.silentStops = 0 - if platform.system() == "Linux": + if platform.system() == 'Linux': if self.startMode_ == AttachCore: pass else: @@ -610,7 +794,7 @@ class Dumper(DumperBase): if self.nativeMixed: self.interpreterEventBreakpoint = \ - self.target.BreakpointCreateByName("qt_qmlDebugMessageAvailable") + self.target.BreakpointCreateByName('qt_qmlDebugMessageAvailable') state = 1 if self.target.IsValid() else 0 self.reportResult('success="%s",msg="%s",exe="%s"' @@ -632,7 +816,7 @@ class Dumper(DumperBase): attachInfo = lldb.SBAttachInfo(self.attachPid_) self.process = self.target.Attach(attachInfo, error) if not error.Success(): - self.reportState("inferiorrunfailed") + self.reportState('inferiorrunfailed') return self.report('pid="%s"' % self.process.GetProcessID()) # Even if it stops it seems that LLDB assumes it is running @@ -641,29 +825,29 @@ class Dumper(DumperBase): if self.process and self.process.GetState() == lldb.eStateStopped: # lldb stops the process after attaching. This happens before the # eventloop starts. Relay the correct state back. - self.reportState("enginerunandinferiorstopok") + self.reportState('enginerunandinferiorstopok') else: - self.reportState("enginerunandinferiorrunok") + self.reportState('enginerunandinferiorrunok') elif self.startMode_ == AttachToRemoteServer or self.startMode_ == AttachToRemoteProcess: self.process = self.target.ConnectRemote( self.debugger.GetListener(), self.remoteChannel_, None, error) if not error.Success(): self.report(self.describeError(error)) - self.reportState("enginerunfailed") + self.reportState('enginerunfailed') return # Even if it stops it seems that LLDB assumes it is running # and later detects that it did stop after all, so it is be # better to mirror that and wait for the spontaneous stop. - self.reportState("enginerunandinferiorrunok") + self.reportState('enginerunandinferiorrunok') elif self.startMode_ == AttachCore: coreFile = args.get('coreFile', ''); self.process = self.target.LoadCore(coreFile) - self.reportState("enginerunokandinferiorunrunnable") + self.reportState('enginerunokandinferiorunrunnable') else: launchInfo = lldb.SBLaunchInfo(self.processArgs_) launchInfo.SetWorkingDirectory(self.workingDirectory_) - environmentList = [key + "=" + value for key,value in os.environ.items()] + environmentList = [key + '=' + value for key,value in os.environ.items()] if self.dyldImageSuffix: environmentList.append('DYLD_IMAGE_SUFFIX=' + self.dyldImageSuffix) if self.dyldLibraryPath: @@ -676,10 +860,10 @@ class Dumper(DumperBase): self.process = self.target.Launch(launchInfo, error) if not error.Success(): self.report(self.describeError(error)) - self.reportState("enginerunfailed") + self.reportState('enginerunfailed') return self.report('pid="%s"' % self.process.GetProcessID()) - self.reportState("enginerunandinferiorrunok") + self.reportState('enginerunandinferiorrunok') def loop(self): event = lldb.SBEvent() @@ -737,11 +921,11 @@ class Dumper(DumperBase): for i in xrange(0, self.process.GetNumThreads()): thread = self.process.GetThreadAtIndex(i) if thread.is_stopped: - state = "stopped" + state = 'stopped' elif thread.is_suspended: - state = "suspended" + state = 'suspended' else: - state = "unknown" + state = 'unknown' reason = thread.GetStopReason() result += '{id="%d"' % thread.GetThreadID() result += ',index="%s"' % i @@ -803,7 +987,7 @@ class Dumper(DumperBase): functionName = frame.GetFunctionName() - if isNativeMixed and functionName == "::qt_qmlDebugMessageAvailable()": + if isNativeMixed and functionName == '::qt_qmlDebugMessageAvailable()': interpreterStack = self.extractInterpreterStack() for interpreterFrame in interpreterStack.get('frames', []): function = interpreterFrame.get('function', '') @@ -843,8 +1027,9 @@ class Dumper(DumperBase): error = lldb.SBError() #warn("READ: %s %s" % (address, size)) res = self.process.ReadMemory(address, size, error) - if res is None: - return bytes() + if res is None or len(res) != size: + # Using code in e.g. readToFirstZero relies on exceptions. + raise RuntimeError("Unreadable %s bytes at 0x%x" % (size, address)) return res def findStaticMetaObject(self, typeName): @@ -919,7 +1104,7 @@ class Dumper(DumperBase): # This can happen for unnamed function parameters with # default values: void foo(int = 0) continue - value = self.fromNativeValue(val) + value = self.fromNativeFrameValue(val) value.name = name variables.append(value) @@ -1120,13 +1305,13 @@ class Dumper(DumperBase): def createBreakpointAtMain(self): return self.target.BreakpointCreateByName( - "main", self.target.GetExecutable().GetFilename()) + 'main', self.target.GetExecutable().GetFilename()) def insertBreakpoint(self, args): - bpType = args["type"] + bpType = args['type'] if bpType == BreakpointByFileAndLine: - fileName = args["file"] - if fileName.endswith(".js") or fileName.endswith(".qml"): + fileName = args['file'] + if fileName.endswith('.js') or fileName.endswith('.qml'): self.insertInterpreterBreakpoint(args) return @@ -1134,11 +1319,11 @@ class Dumper(DumperBase): more = True if bpType == BreakpointByFileAndLine: bp = self.target.BreakpointCreateByLocation( - str(args["file"]), int(args["line"])) + str(args['file']), int(args['line'])) elif bpType == BreakpointByFunction: - bp = self.target.BreakpointCreateByName(args["function"]) + bp = self.target.BreakpointCreateByName(args['function']) elif bpType == BreakpointByAddress: - bp = self.target.BreakpointCreateByAddress(args["address"]) + bp = self.target.BreakpointCreateByAddress(args['address']) elif bpType == BreakpointAtMain: bp = self.createBreakpointAtMain() elif bpType == BreakpointAtThrow: @@ -1150,14 +1335,14 @@ class Dumper(DumperBase): elif bpType == WatchpointAtAddress: error = lldb.SBError() # This might yield bp.IsValid() == False and - # error.desc == "process is not alive". - bp = self.target.WatchAddress(args["address"], 4, False, True, error) + # error.desc == 'process is not alive'. + bp = self.target.WatchAddress(args['address'], 4, False, True, error) extra = self.describeError(error) elif bpType == WatchpointAtExpression: # FIXME: Top level-only for now. try: frame = self.currentFrame() - value = frame.FindVariable(args["expression"]) + value = frame.FindVariable(args['expression']) error = lldb.SBError() bp = self.target.WatchAddress(value.GetLoadAddress(), value.GetByteSize(), False, True, error) @@ -1165,45 +1350,45 @@ class Dumper(DumperBase): bp = self.target.BreakpointCreateByName(None) else: # This leaves the unhandled breakpoint in a (harmless) - # "pending" state. + # 'pending' state. bp = self.target.BreakpointCreateByName(None) more = False if more and bp.IsValid(): - bp.SetIgnoreCount(int(args["ignorecount"])) - bp.SetCondition(self.hexdecode(args["condition"])) - bp.SetEnabled(bool(args["enabled"])) + bp.SetIgnoreCount(int(args['ignorecount'])) + bp.SetCondition(self.hexdecode(args['condition'])) + bp.SetEnabled(bool(args['enabled'])) bp.SetScriptCallbackBody('\n'.join([ - "def foo(frame = frame, bp_loc = bp_loc, dict = internal_dict):", - " " + self.hexdecode(args["command"]).replace('\n', '\n '), - "from cStringIO import StringIO", - "origout = sys.stdout", - "sys.stdout = StringIO()", - "result = foo()", - "d = lldb.theDumper", - "output = d.hexencode(sys.stdout.getvalue())", - "sys.stdout = origout", - "d.report('output={channel=\"stderr\",data=\"' + output + '\"}')", - "if result is False:", - " d.reportState('continueafternextstop')", - "return True" + 'def foo(frame = frame, bp_loc = bp_loc, dict = internal_dict):', + ' ' + self.hexdecode(args['command']).replace('\n', '\n '), + 'from cStringIO import StringIO', + 'origout = sys.stdout', + 'sys.stdout = StringIO()', + 'result = foo()', + 'd = lldb.theDumper', + 'output = d.hexencode(sys.stdout.getvalue())', + 'sys.stdout = origout', + 'd.report("output={channel=\"stderr\",data=\"' + output + '\"}")', + 'if result is False:', + ' d.reportState("continueafternextstop")', + 'return True' ])) if isinstance(bp, lldb.SBBreakpoint): - bp.SetOneShot(bool(args["oneshot"])) + bp.SetOneShot(bool(args['oneshot'])) self.reportResult(self.describeBreakpoint(bp) + extra, args) def changeBreakpoint(self, args): - lldbId = int(args["lldbid"]) + lldbId = int(args['lldbid']) if lldbId > qqWatchpointOffset: bp = self.target.FindWatchpointByID(lldbId) else: bp = self.target.FindBreakpointByID(lldbId) if bp.IsValid(): - bp.SetIgnoreCount(int(args["ignorecount"])) - bp.SetCondition(self.hexdecode(args["condition"])) - bp.SetEnabled(bool(args["enabled"])) + bp.SetIgnoreCount(int(args['ignorecount'])) + bp.SetCondition(self.hexdecode(args['condition'])) + bp.SetEnabled(bool(args['enabled'])) if isinstance(bp, lldb.SBBreakpoint): - bp.SetOneShot(bool(args["oneshot"])) + bp.SetOneShot(bool(args['oneshot'])) self.reportResult(self.describeBreakpoint(bp), args) def removeBreakpoint(self, args): @@ -1269,16 +1454,16 @@ class Dumper(DumperBase): def shutdownInferior(self, args): self.isShuttingDown_ = True if self.process is None: - self.reportState("inferiorshutdownok") + self.reportState('inferiorshutdownok') else: state = self.process.GetState() if state == lldb.eStateStopped: self.process.Kill() - self.reportState("inferiorshutdownok") + self.reportState('inferiorshutdownok') self.reportResult('', args) def quit(self, args): - self.reportState("engineshutdownok") + self.reportState('engineshutdownok') self.process.Kill() self.reportResult('', args) @@ -1299,7 +1484,7 @@ class Dumper(DumperBase): bp = self.target.BreakpointCreateByAddress(addr) if bp.GetNumLocations() == 0: self.target.BreakpointDelete(bp.GetID()) - self.reportResult(self.describeStatus("No target location found.") + self.reportResult(self.describeStatus('No target location found.') + self.describeLocation(frame), args) return bp.SetOneShot(True) @@ -1311,14 +1496,14 @@ class Dumper(DumperBase): line = int(args['line']) error = self.currentThread().StepOverUntil(frame, lldb.SBFileSpec(file), line) self.reportResult(self.describeError(error), args) - self.reportState("running") - self.reportState("stopped") + self.reportState('running') + self.reportState('stopped') def executeJumpToLocation(self, args): self.reportToken(args) frame = self.currentFrame() if not frame: - self.reportResult(self.describeStatus("No frame available."), args) + self.reportResult(self.describeStatus('No frame available.'), args) return addr = args.get('address', 0) if addr: @@ -1328,17 +1513,17 @@ class Dumper(DumperBase): str(args['file']), int(args['line'])) if bp.GetNumLocations() == 0: self.target.BreakpointDelete(bp.GetID()) - status = "No target location found." + status = 'No target location found.' else: loc = bp.GetLocationAtIndex(0) self.target.BreakpointDelete(bp.GetID()) res = frame.SetPC(loc.GetLoadAddress()) - status = "Jumped." if res else "Cannot jump." + status = 'Jumped.' if res else 'Cannot jump.' self.reportResult(self.describeStatus(status) + self.describeLocation(frame), args) def breakList(self): result = lldb.SBCommandReturnObject() - self.debugger.GetCommandInterpreter().HandleCommand("break list", result) + self.debugger.GetCommandInterpreter().HandleCommand('break list', result) self.report('success="%d",output="%s",error="%s"' % (result.Succeeded(), result.GetOutput(), result.GetError())) @@ -1354,7 +1539,7 @@ class Dumper(DumperBase): self.reportResult('', args) def fetchFullBacktrace(self, _ = None): - command = "thread backtrace all" + command = 'thread backtrace all' result = lldb.SBCommandReturnObject() self.debugger.GetCommandInterpreter().HandleCommand(command, result) self.reportResult(self.hexencode(result.GetOutput()), {}) @@ -1385,7 +1570,7 @@ class Dumper(DumperBase): else: base = args.get('address', 0) if int(base) == 0xffffffffffffffff: - warn("INVALID DISASSEMBLER BASE") + warn('INVALID DISASSEMBLER BASE') return addr = lldb.SBAddress(base, self.target) instructions = self.target.ReadInstructions(addr, 100) @@ -1406,7 +1591,7 @@ class Dumper(DumperBase): if lineNumber != currentLine or fileName != currentFile: currentLine = lineNumber currentFile = fileName - key = "%s:%s" % (fileName, lineNumber) + key = '%s:%s' % (fileName, lineNumber) hunk = hunks.get(key, 0) + 1 hunks[key] = hunk source = sources.get(fileName, None) @@ -1419,8 +1604,8 @@ class Dumper(DumperBase): # With lldb-3.8 files like /data/dev/creator-3.6/tests/ # auto/debugger/qt_tst_dumpers_StdVector_bfNWZa/main.cpp # with non-existent directories appear. - warn("FILE: %s ERROR: %s" % (fileName, error)) - source = "" + warn('FILE: %s ERROR: %s' % (fileName, error)) + source = '' result += '{line="%s"' % lineNumber result += ',file="%s"' % fileName if 0 < lineNumber and lineNumber <= len(source): @@ -1463,7 +1648,7 @@ class Dumper(DumperBase): self.reportResult(self.describeError(error), args) def createResolvePendingBreakpointsHookBreakpoint(self, args): - bp = self.target.BreakpointCreateByName("qt_qmlDebugConnectorOpen") + bp = self.target.BreakpointCreateByName('qt_qmlDebugConnectorOpen') bp.SetOneShot(True) self.interpreterBreakpointResolvers.append( lambda: self.resolvePendingInterpreterBreakpoint(args)) @@ -1479,7 +1664,7 @@ class Tester(Dumper): self.target = self.debugger.CreateTarget(binary, None, None, True, error) if error.GetType(): - warn("ERROR: %s" % error) + warn('ERROR: %s' % error) return s = threading.Thread(target=self.testLoop, args=(args,)) @@ -1494,19 +1679,19 @@ class Tester(Dumper): error = lldb.SBError() launchInfo = lldb.SBLaunchInfo([]) launchInfo.SetWorkingDirectory(os.getcwd()) - environmentList = [key + "=" + value for key,value in os.environ.items()] + environmentList = [key + '=' + value for key,value in os.environ.items()] launchInfo.SetEnvironmentEntries(environmentList, False) self.process = self.target.Launch(launchInfo, error) if error.GetType(): - warn("ERROR: %s" % error) + warn('ERROR: %s' % error) event = lldb.SBEvent() listener = self.debugger.GetListener() while True: state = self.process.GetState() if listener.WaitForEvent(100, event): - #warn("EVENT: %s" % event) + #warn('EVENT: %s' % event) state = lldb.SBProcess.GetStateFromEvent(event) if state == lldb.eStateExited: # 10 break @@ -1515,14 +1700,14 @@ class Tester(Dumper): for i in xrange(0, self.process.GetNumThreads()): thread = self.process.GetThreadAtIndex(i) reason = thread.GetStopReason() - #warn("THREAD: %s REASON: %s" % (thread, reason)) + #warn('THREAD: %s REASON: %s' % (thread, reason)) if (reason == lldb.eStopReasonBreakpoint or reason == lldb.eStopReasonException or reason == lldb.eStopReasonSignal): stoppedThread = thread if stoppedThread: - # This seems highly fragile and depending on the "No-ops" in the + # This seems highly fragile and depending on the 'No-ops' in the # event handling above. frame = stoppedThread.GetFrameAtIndex(0) line = frame.line_entry.line @@ -1531,13 +1716,13 @@ class Tester(Dumper): self.process.SetSelectedThread(stoppedThread) self.fetchVariables(args) #self.describeLocation(frame) - self.report("@NS@%s@" % self.qtNamespace()) - #self.report("ENV=%s" % os.environ.items()) - #self.report("DUMPER=%s" % self.qqDumpers) + self.report('@NS@%s@' % self.qtNamespace()) + #self.report('ENV=%s' % os.environ.items()) + #self.report('DUMPER=%s' % self.qqDumpers) break else: warn('TIMEOUT') - warn("Cannot determined stopped thread") + warn('Cannot determined stopped thread') lldb.SBDebugger.Destroy(self.debugger) diff --git a/share/qtcreator/debugger/misctypes.py b/share/qtcreator/debugger/misctypes.py index 0a789d64fa4..6a32cd05bd6 100644 --- a/share/qtcreator/debugger/misctypes.py +++ b/share/qtcreator/debugger/misctypes.py @@ -121,7 +121,7 @@ def qdump__Eigen__Matrix(d, value): storage = value['m_storage'] nrows = storage['m_rows'].integer() if argRow == -1 else argRow ncols = storage['m_cols'].integer() if argCol == -1 else argCol - p = storage['m_data'].integer() + p = storage['m_data'].pointer() innerSize = innerType.size() d.putValue('(%s x %s), %s' % (nrows, ncols, ['ColumnMajor', 'RowMajor'][rowMajor])) d.putField('keeporder', '1') diff --git a/share/qtcreator/debugger/qttypes.py b/share/qtcreator/debugger/qttypes.py index 6e8cb59892e..bb53b4af826 100644 --- a/share/qtcreator/debugger/qttypes.py +++ b/share/qtcreator/debugger/qttypes.py @@ -152,7 +152,7 @@ def qdump_X_QModelIndex(d, value): except: p = value['i'] m = value['m'] - if m.integer() == 0 or r < 0 or c < 0: + if m.pointer() == 0 or r < 0 or c < 0: d.putValue('(invalid)') d.putPlainChildren(value) return @@ -196,7 +196,7 @@ def qdump_X_QModelIndex(d, value): def qdump__QDate(d, value): - jd = value.integer() + jd = value.pointer() if jd: d.putValue(jd, 'juliandate') d.putNumChild(1) @@ -332,6 +332,7 @@ def qdump__QDateTime(d, value): d.putNumChild(0) return + d.putNumChild(1) if d.isExpanded(): with Children(d): ns = d.qtNamespace() @@ -1120,7 +1121,7 @@ def qdump__QRegion(d, value): def qdump__QScopedPointer(d, value): - if value.integer() == 0: + if value.pointer() == 0: d.putValue('(null)') d.putNumChild(0) else: @@ -1187,13 +1188,13 @@ def qdump__QSet(d, value): def qdump__QSharedData(d, value): - d.putValue('ref: %s' % d.extractInt(value['ref'].address)) + d.putValue('ref: %s' % value.to('i')) d.putNumChild(0) def qdump__QSharedDataPointer(d, value): d_ptr = value['d'] - if d_ptr.integer() == 0: + if d_ptr.pointer() == 0: d.putValue('(null)') d.putNumChild(0) else: @@ -1206,7 +1207,7 @@ def qdump__QSharedDataPointer(d, value): d.putPlainChildren(value) return d.putBetterType(d.currentType) - d.putItem(d_ptr.cast(innerType.pointer()).dereference()) + d.putItem(d_ptr.dereference()) @@ -1404,6 +1405,7 @@ def qdump__QUrl(d, value): if displayFormat == SeparateFormat: d.putDisplay('utf16:separate', url) + d.putNumChild(1) if d.isExpanded(): with Children(d): d.putIntItem('port', port) @@ -1684,12 +1686,12 @@ def qdump__QVariant(d, value): ptr = p.pointer() (elided, blob) = d.encodeCArray(ptr, 1, 100) typeName = d.hexdecode(blob) - base = data.extractPointer() # Prefer namespaced version. if len(ns) > 0: if not d.lookupNativeType(ns + typeName) is None: typeName = ns + typeName - d.putSubItem('data', d.createValue(base, d.createType(typeName))) + data.type = d.createType(typeName + ' *') + d.putSubItem('data', data) if not typeName is None: d.putBetterType('%sQVariant (%s)' % (ns, typeName)) return None @@ -1706,7 +1708,7 @@ def qedit__QVector(d, value, data): base = value['d'].address() + offset except: # Qt 4. - base = value['p']['array'].integer() + base = value['p']['array'].pointer() d.setValues(base, innerType, values) @@ -1766,6 +1768,7 @@ def qdump_QWeakPointerHelper(d, value, isWeak): def qdump__QXmlAttributes__Attribute(d, value): d.putEmptyValue() + d.putNumChild(1) if d.isExpanded(): with Children(d): (qname, uri, localname, val) = value.split('{QString}' * 4) @@ -2345,7 +2348,7 @@ def qdump__QScriptValue(d, value): #d.putEmptyValue() dd = value['d_ptr']['d'] ns = d.qtNamespace() - if dd.integer() == 0: + if dd.pointer() == 0: d.putValue('(invalid)') d.putNumChild(0) return @@ -2621,9 +2624,9 @@ def qdump__QJsonValue(d, value): def qdump__QJsonArray(d, value): - qdumpHelper_QJsonArray(d, value['d'].integer(), value['a'].integer()) + qdumpHelper_QJsonArray(d, value['d'].pointer(), value['a'].pointer()) def qdump__QJsonObject(d, value): - qdumpHelper_QJsonObject(d, value['d'].integer(), value['o'].integer()) + qdumpHelper_QJsonObject(d, value['d'].pointer(), value['o'].pointer()) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index 2733253bf45..a1b5a0a6992 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -76,17 +76,17 @@ def qdump__std__deque(d, value): impl = value["_M_impl"] start = impl["_M_start"] finish = impl["_M_finish"] - size = bufsize * int((finish["_M_node"].integer() - start["_M_node"].integer()) / d.ptrSize() - 1) - size += int((finish["_M_cur"].integer() - finish["_M_first"].integer()) / innerSize) - size += int((start["_M_last"].integer() - start["_M_cur"].integer()) / innerSize) + size = bufsize * ((finish["_M_node"].pointer() - start["_M_node"].pointer()) // d.ptrSize() - 1) + size += ((finish["_M_cur"].pointer() - finish["_M_first"].pointer()) // innerSize) + size += ((start["_M_last"].pointer() - start["_M_cur"].pointer()) // innerSize) d.check(0 <= size and size <= 1000 * 1000 * 1000) d.putItemCount(size) if d.isExpanded(): with Children(d, size, maxNumChild=2000, childType=innerType): - pcur = start["_M_cur"].integer() + pcur = start["_M_cur"].pointer() pfirst = start["_M_first"] - plast = start["_M_last"].integer() + plast = start["_M_last"].pointer() pnode = start["_M_node"] for i in d.childRange(): d.putSubItem(i, d.createValue(pcur, innerType)) @@ -99,7 +99,7 @@ def qdump__std__deque(d, value): #warn("NEWNODE: 0x%x %s" % (newnode.pointer(), newnode)) pnode = newnode #warn("PNODE 2: 0x%x %s" % (pnode.pointer(), pnode)) - pfirst = newnode.dereference().integer() + pfirst = newnode.dereference().pointer() plast = pfirst + bufsize * d.ptrSize() pcur = pfirst @@ -237,7 +237,6 @@ def qdump__std__map(d, value): if d.isExpanded(): pairType = value.type[3][0] - pairPointer = pairType.pointer() with PairedChildren(d, size, pairType=pairType, maxNumChild=1000): node = value["_M_t"]["_M_impl"]["_M_header"]["_M_left"] nodeSize = node.dereference().type.size() @@ -245,10 +244,10 @@ def qdump__std__map(d, value): for i in d.childRange(): (pad1, key, pad2, value) = d.split(typeCode, node.pointer() + nodeSize) d.putPairItem(i, (key, value)) - if node["_M_right"].integer() == 0: + if node["_M_right"].pointer() == 0: parent = node["_M_parent"] while True: - if node.integer() != parent["_M_right"].integer(): + if node.pointer() != parent["_M_right"].pointer(): break node = parent parent = parent["_M_parent"] @@ -257,7 +256,7 @@ def qdump__std__map(d, value): else: node = node["_M_right"] while True: - if node["_M_left"].integer() == 0: + if node["_M_left"].pointer() == 0: break node = node["_M_left"] @@ -271,13 +270,13 @@ def qdump_std__map__helper(d, size, value): for i in d.childRange(): pair = node.cast(nodeType).dereference()['_Myval'] d.putPairItem(i, pair) - if node['_Right']['_Isnil'].integer() == 0: + if node['_Right']['_Isnil'].pointer() == 0: node = node['_Right'] - while node['_Left']['_Isnil'].integer() == 0: + while node['_Left']['_Isnil'].pointer() == 0: node = node['_Left'] else: parent = node['_Parent'] - while node and parent['_Right']['_Isnil'].integer() == 0: + while node and parent['_Right']['_Isnil'].pointer() == 0: node = parent parent = parent['_Parent'] if node['_Right'] != parent: @@ -377,7 +376,7 @@ def qdump__std__set(d, value): for i in d.childRange(): (pad, val) = d.split(typeCode, node.pointer() + nodeSize) d.putSubItem(i, val) - if node["_M_right"].integer() == 0: + if node["_M_right"].pointer() == 0: parent = node["_M_parent"] while node == parent["_M_right"]: node = parent @@ -386,7 +385,7 @@ def qdump__std__set(d, value): node = parent else: node = node["_M_right"] - while node["_M_left"].integer() != 0: + while node["_M_left"].pointer() != 0: node = node["_M_left"] def qdump__std__set__QNX(d, value): @@ -419,7 +418,7 @@ def std1TreeMin(d, node): # return __x; # left = node['__left_'] - if left.integer(): + if left.pointer(): node = left return node @@ -428,7 +427,7 @@ def std1TreeIsLeftChild(d, node): # return __x == __x->__parent_->__left_; # other = node['__parent_']['__left_'] - return node.integer() == other.integer() + return node.pointer() == other.pointer() def std1TreeNext(d, node): @@ -440,7 +439,7 @@ def std1TreeNext(d, node): # return __x->__parent_; # right = node['__right_'] - if right.integer(): + if right.pointer(): return std1TreeMin(d, right) while not std1TreeIsLeftChild(d, node): node = node['__parent_'] @@ -612,18 +611,18 @@ def qdump__std____1__wstring(d, value): def qdump__std__shared_ptr(d, value): - if d.isMsvcTarget: + if d.isMsvcTarget(): i = value["_Ptr"] else: i = value["_M_ptr"] - if i.integer() == 0: + if i.pointer() == 0: d.putValue("(null)") d.putNumChild(0) return with Children(d): - short = d.putSubItem("data", i) - if d.isMsvcTarget: + short = d.putSubItem("data", i.dereference()) + if d.isMsvcTarget(): refcount = value["_Rep"] d.putIntItem("usecount", refcount["_Uses"]) d.putIntItem("weakcount", refcount["_Weaks"]) @@ -635,7 +634,7 @@ def qdump__std__shared_ptr(d, value): def qdump__std____1__shared_ptr(d, value): i = value["__ptr_"] - if i.integer() == 0: + if i.pointer() == 0: d.putValue("(null)") d.putNumChild(0) return @@ -660,6 +659,17 @@ def qdump__std____1__unique_ptr(d, value): qdump__std__unique_ptr(d, value) +def qdump__std__pair(d, value): + typeCode = '{%s}@{%s}' % (value.type[0].name, value.type[1].name) + first, pad, second = value.split(typeCode) + with Children(d): + key = d.putSubItem('first', first) + value = d.putSubItem('second', second) + d.putField('key', key.value) + if key.encoding is not None: + d.putField('keyencoded', key.encoding) + d.putValue(value.value, value.encoding) + def qform__std__unordered_map(): return mapForms() @@ -733,7 +743,7 @@ def qdump__std__unordered_set(d, value): d.putItemCount(size) if d.isExpanded(): - p = start.integer() + p = start.pointer() valueType = value.type[0] with Children(d, size, childType=valueType): ptrSize = d.ptrSize() @@ -974,10 +984,10 @@ def qdump____gnu_cxx__hash_set(d, value): bucketFinish = buckets["_M_finish"] p = bucketStart itemCount = 0 - for i in xrange(int((bucketFinish.integer() - bucketStart.integer()) / d.ptrSize())): - if p.dereference().integer(): + for i in xrange((bucketFinish.pointer() - bucketStart.pointer()) // d.ptrSize()): + if p.dereference().pointer(): cur = p.dereference() - while cur.integer(): + while cur.pointer(): d.putSubItem(itemCount, cur["_M_val"]) cur = cur["_M_next"] itemCount += 1 diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index c3d0437bf6a..6bd10a2e0b8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -605,7 +605,33 @@ ObjectNodeInstance::Pointer ObjectNodeInstance::create(QObject *object) QObject *ObjectNodeInstance::createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context) { - return QmlPrivateGate::createPrimitive(typeName, majorNumber, minorNumber, context); + QObject *object = QmlPrivateGate::createPrimitive(typeName, majorNumber, minorNumber, context); + + /* Let's try to create the primitive from source, since with incomplete meta info this might be a pure + * QML type. This is the case for example if a C++ type is mocked up with a QML file. + */ + + if (!object) + object = createPrimitiveFromSource(typeName, majorNumber, minorNumber, context); + + return object; +} + +QObject *ObjectNodeInstance::createPrimitiveFromSource(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context) +{ + if (typeName.isEmpty()) + return 0; + + QStringList parts = typeName.split("/"); + const QString unqualifiedTypeName = parts.last(); + parts.removeLast(); + + if (parts.isEmpty()) + return 0; + + const QString importString = parts.join(".") + " " + QString::number(majorNumber) + "." + QString::number(minorNumber); + QString source = "import " + importString + "\n" + unqualifiedTypeName + " {\n" + "}\n"; + return createCustomParserObject(source, "", context); } QObject *ObjectNodeInstance::createComponentWrap(const QString &nodeSource, const QByteArray &importCode, QQmlContext *context) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h index cecc9c8ee3c..8266f0addf8 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h @@ -67,6 +67,7 @@ public: static Pointer create(QObject *objectToBeWrapped); static QObject *createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context); + static QObject *createPrimitiveFromSource(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context); static QObject *createCustomParserObject(const QString &nodeSource, const QByteArray &importCode, QQmlContext *context); static QObject *createComponent(const QString &componentPath, QQmlContext *context); static QObject *createComponent(const QUrl &componentUrl, QQmlContext *context); diff --git a/share/qtcreator/templates/wizards/projects/plainc/CMakeLists.txt b/share/qtcreator/templates/wizards/projects/plainc/CMakeLists.txt index 9001db70166..f4783c4195d 100644 --- a/share/qtcreator/templates/wizards/projects/plainc/CMakeLists.txt +++ b/share/qtcreator/templates/wizards/projects/plainc/CMakeLists.txt @@ -1,4 +1,4 @@ -project(%{ProjectName}) cmake_minimum_required(VERSION 2.8) -aux_source_directory(. SRC_LIST) -add_executable(${PROJECT_NAME} ${SRC_LIST}) + +project(%{ProjectName}) +add_executable(${PROJECT_NAME} "%{CFileName}") diff --git a/share/qtcreator/templates/wizards/projects/plaincpp/CMakeLists.txt b/share/qtcreator/templates/wizards/projects/plaincpp/CMakeLists.txt index 9001db70166..6393118931f 100644 --- a/share/qtcreator/templates/wizards/projects/plaincpp/CMakeLists.txt +++ b/share/qtcreator/templates/wizards/projects/plaincpp/CMakeLists.txt @@ -1,4 +1,4 @@ -project(%{ProjectName}) cmake_minimum_required(VERSION 2.8) -aux_source_directory(. SRC_LIST) -add_executable(${PROJECT_NAME} ${SRC_LIST}) + +project(%{ProjectName}) +add_executable(${PROJECT_NAME} "%{CppFileName}") diff --git a/share/qtcreator/templates/wizards/qtquick2-extension/project.pro b/share/qtcreator/templates/wizards/qtquick2-extension/project.pro index cd8c68954c6..36df40ced98 100644 --- a/share/qtcreator/templates/wizards/qtquick2-extension/project.pro +++ b/share/qtcreator/templates/wizards/qtquick2-extension/project.pro @@ -1,7 +1,7 @@ TEMPLATE = lib TARGET = %ProjectName% QT += qml quick -CONFIG += qt plugin c++11 +CONFIG += plugin c++11 TARGET = $$qtLibraryTarget($$TARGET) uri = %Uri% diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index ff8a66cf1c3..b75b3e6ea69 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -242,5 +242,5 @@ IconOverlayCppSource=:/cppeditor/images/dark_qt_cpp.png IconOverlayPrf=:/qtsupport/images/dark_qt_project.png IconOverlayPri=:/qtsupport/images/dark_qt_project.png IconOverlayPro=:/qtsupport/images/dark_qt_project.png -StandardPixmapFileIcon=:/core/images/dark_fileicon.png -StandardPixmapDirIcon=:/core/images/dark_foldericon.png +StandardPixmapFileIcon=:/utils/images/dark_fileicon.png +StandardPixmapDirIcon=:/utils/images/dark_foldericon.png diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index 7b44ddb9e3e..5d63556c128 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -19865,6 +19865,38 @@ Ids must begin with a lowercase letter. iOS tool Error %1 Ошибка %1 утилиты iOS + + Application install on Simulator failed. %1 + Не удалось установить приложение на эмулятор. %1 + + + Application install on Simulator failed. Simulator not running. + Не удалось установить приложение на эмулятор. Он не запущен. + + + Application launch on Simulator failed. Invalid Bundle path %1 + Запуск приложения на эмуляторе не удался. Неверный путь пакета %1 + + + Spawning the Application process on Simulator failed. + Не удалось породить процесс приложения на эмуляторе. + + + Application launch on Simulator failed. Simulator not running. + Не удалось запустить приложение на эмуляторе. Он не запущен. + + + Application launch on Simulator failed. %1 + Запуск приложения на эмуляторе не удался. %1 + + + Spawning the Application process on Simulator failed. Spawning timed out. + Не удалось породить процесс приложения на эмуляторе. Время порождения истекло. + + + Simulator application process error %1 + Приложение из эмулятора вернуло ошибку %1 + IosDeployStepWidget @@ -21336,14 +21368,14 @@ Ids must begin with a lowercase letter. Nim::NimCompilerBuildStepFactory Nim Compiler Build Step - Этап сборки компилятора Nim + Этап сборки компилятора Nim Nim::NimCompilerCleanStep Nim Clean Step - Этап очистки компилятора Nim + Этап очистки компилятора Nim Build directory "%1" does not exist. @@ -21432,28 +21464,28 @@ Ids must begin with a lowercase letter. NimCompilerBuildStep Nim Compiler Build Step - Этап сборки компилятора Nim + Этап сборки компилятора Nim NimCompilerBuildStepConfigWidget Nim build step - Этап сборки Nim + Этап сборки Nim NimCompilerCleanStepFactory Nim Compiler Clean Step - Этап очистки компилятора Nim + Этап очистки компилятора Nim NimCompilerCleanStepWidget Nim clean step - Этап очистки Nim + Этап очистки Nim @@ -36642,11 +36674,11 @@ In addition, Shift+Enter inserts an escape character at the cursor position and Remove the automatically inserted character if the trigger is deleted by backspace after the completion. - Удалять автоматически вставленный символ, если флаг удалён бекспейсом после дополнения. + Удалять автоматически вставленный символ, если флаг удалён бекспейсом после дополнения. Remove automatically inserted text on backspace - Удалять автоматически вставленный текст по бекспейсу + Удалять автоматически вставленный текст по бекспейсу Documentation Comments diff --git a/share/qtcreator/welcomescreen/examples.qml b/share/qtcreator/welcomescreen/examples.qml index 063a1f5a49b..99476d8817b 100644 --- a/share/qtcreator/welcomescreen/examples.qml +++ b/share/qtcreator/welcomescreen/examples.qml @@ -66,9 +66,9 @@ Item { anchors.leftMargin: 18 anchors.rightMargin: 20 anchors.right: parent.right - + text: examplesModel.searchString placeholderText: qsTr("Search in Examples...") - onTextChanged: examplesModel.parseSearchString(text) + onTextChanged: examplesModel.setSearchString(text) } CustomizedGridView { diff --git a/share/qtcreator/welcomescreen/tutorials.qml b/share/qtcreator/welcomescreen/tutorials.qml index 4c8535bedb6..eb227bf5117 100644 --- a/share/qtcreator/welcomescreen/tutorials.qml +++ b/share/qtcreator/welcomescreen/tutorials.qml @@ -39,9 +39,9 @@ Item { anchors.rightMargin: 20 anchors.left: parent.left anchors.leftMargin: 30 - + text: tutorialsModel.searchString placeholderText: qsTr("Search in Tutorials...") - onTextChanged: tutorialsModel.parseSearchString(text) + onTextChanged: tutorialsModel.setSearchString(text) } CustomizedGridView { diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index c9c136d1781..936beec915f 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1252,7 +1252,12 @@ bool Check::visit(BinaryExpression *ast) bool Check::visit(Block *ast) { - addMessage(ErrBlocksNotSupportedInQmlUi, locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation())); + + bool isDirectInConnectionsScope = + (!m_typeStack.isEmpty() && m_typeStack.last() == QLatin1String("Connections")); + + if (!isDirectInConnectionsScope) + addMessage(ErrBlocksNotSupportedInQmlUi, locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation())); if (Node *p = parent()) { if (!cast(p) diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index e0983433916..34d1cd24959 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -1300,7 +1300,7 @@ void ModelManagerInterface::updateCppQmlTypes(QFutureInterface &interface, interface.setProgressValue(0); CppDataHash newData; - QHash newDeclarations; + QHash > newDeclarations; { QMutexLocker locker(&qmlModelManager->m_cppDataMutex); newData = qmlModelManager->m_cppDataHash; @@ -1321,33 +1321,36 @@ void ModelManagerInterface::updateCppQmlTypes(QFutureInterface &interface, const QString fileName = doc->fileName(); if (!scan) { hasNewInfo = newData.remove(fileName) > 0 || hasNewInfo; - foreach (const QString &file, newDeclarations[fileName]) { - CPlusPlus::Document::Ptr doc = snapshot.document(file); - if (doc.isNull()) - continue; - finder(doc); - hasNewInfo = rescanExports(file, finder, newData) || hasNewInfo; + foreach (const CPlusPlus::Document::Ptr &savedDoc, newDeclarations.value(fileName)) { + finder(savedDoc); + hasNewInfo = rescanExports(savedDoc->fileName(), finder, newData) || hasNewInfo; } continue; } for (auto it = newDeclarations.begin(), end = newDeclarations.end(); it != end;) { - if (it->removeOne(fileName)) { - doc->releaseSourceAndAST(); - if (it->isEmpty()) { - it = newDeclarations.erase(it); - continue; + for (auto docIt = it->begin(), endDocIt = it->end(); docIt != endDocIt;) { + CPlusPlus::Document::Ptr &savedDoc = *docIt; + if (savedDoc->fileName() == fileName) { + savedDoc->releaseSourceAndAST(); + it->erase(docIt); + break; + } else { + ++docIt; } } - ++it; + if (it->isEmpty()) + it = newDeclarations.erase(it); + else + ++it; } foreach (const QString &declarationFile, finder(doc)) { - newDeclarations[declarationFile].append(fileName); + newDeclarations[declarationFile].append(doc); doc->keepSourceAndAST(); // keep for later reparsing when dependent doc changes } - hasNewInfo = rescanExports(doc->fileName(), finder, newData) || hasNewInfo; + hasNewInfo = rescanExports(fileName, finder, newData) || hasNewInfo; doc->releaseSourceAndAST(); } diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h index 3f640c6ca78..2cb02083f6e 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.h +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h @@ -275,7 +275,7 @@ private: QrcCache m_qrcCache; CppDataHash m_cppDataHash; - QHash m_cppDeclarationFiles; + QHash > m_cppDeclarationFiles; mutable QMutex m_cppDataMutex; // project integration diff --git a/src/libs/qtcreatorcdbext/cdb_detect.pri b/src/libs/qtcreatorcdbext/cdb_detect.pri index 286d9f20c30..ae09e258d4d 100644 --- a/src/libs/qtcreatorcdbext/cdb_detect.pri +++ b/src/libs/qtcreatorcdbext/cdb_detect.pri @@ -14,9 +14,9 @@ msvc { # Starting from Windows SDK 8, the headers and libs are under 'ProgramFiles (x86)'. # The libraries are under 'ProgramFiles'as well, so, check for existence of 'inc'. # 32bit qmake: - !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Windows Kits/8.0/Debuggers" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Windows Kits/8.1/Debuggers" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Windows Kits/10/Debuggers" + !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles)/Windows Kits/8.0/Debuggers" + !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles)/Windows Kits/8.1/Debuggers" + !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles)/Windows Kits/10/Debuggers" # 64bit qmake: !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles) (x86)/Windows Kits/8.0/Debuggers" !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles) (x86)/Windows Kits/8.1/Debuggers" diff --git a/src/libs/ssh/sftpfilesystemmodel.cpp b/src/libs/ssh/sftpfilesystemmodel.cpp index 922ca57b54e..d3432c5528e 100644 --- a/src/libs/ssh/sftpfilesystemmodel.cpp +++ b/src/libs/ssh/sftpfilesystemmodel.cpp @@ -166,11 +166,11 @@ QVariant SftpFileSystemModel::data(const QModelIndex &index, int role) const switch (node->fileInfo.type) { case FileTypeRegular: case FileTypeOther: - return QIcon(QLatin1String(":/core/images/unknownfile.png")); + return QIcon(":/utils/images/unknownfile.png"); case FileTypeDirectory: - return QIcon(QLatin1String(":/core/images/dir.png")); + return QIcon(":/utils/images/dir.png"); case FileTypeUnknown: - return QIcon(QLatin1String(":/core/images/help.png")); // Shows a question mark. + return QIcon(":/utils/images/help.png"); // Shows a question mark. } } if (index.column() == 1) { diff --git a/src/libs/utils/runextensions.h b/src/libs/utils/runextensions.h index 43a25411d6e..0ba0b8c3c53 100644 --- a/src/libs/utils/runextensions.h +++ b/src/libs/utils/runextensions.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -493,4 +494,58 @@ runAsync(QThreadPool *pool, Function &&function, Args&&... args) std::forward(args)...); } + +/*! + Adds a handler for when a result is ready. + This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions + or create a QFutureWatcher already for other reasons. +*/ +template +const QFuture &onResultReady(const QFuture &future, R *receiver, void(R::*member)(const T &)) +{ + auto watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); + QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, receiver, + [receiver, member, watcher](int index) { + (receiver->*member)(watcher->future().resultAt(index)); + }); + return future; +} + +/*! + Adds a handler for when a result is ready. The guard object determines the lifetime of + the connection. + This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions + or create a QFutureWatcher already for other reasons. +*/ +template +const QFuture &onResultReady(const QFuture &future, QObject *guard, Function f) +{ + auto watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); + QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, guard, [f, watcher](int index) { + f(watcher->future().resultAt(index)); + }); + return future; +} + +/*! + Adds a handler for when a result is ready. + This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions + or create a QFutureWatcher already for other reasons. +*/ +template +const QFuture &onResultReady(const QFuture &future, Function f) +{ + auto watcher = new QFutureWatcher(); + watcher->setFuture(future); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); + QObject::connect(watcher, &QFutureWatcherBase::resultReadyAt, [f, watcher](int index) { + f(watcher->future().resultAt(index)); + }); + return future; +} + } // Utils diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 0b8970423b6..a859871d542 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -149,9 +149,7 @@ public: PaletteShadow, PaletteWindowDisabled, - PaletteBackgroundDisabled, PaletteWindowTextDisabled, - PaletteForegroundDisabled, PaletteBaseDisabled, PaletteAlternateBaseDisabled, PaletteToolTipBaseDisabled, @@ -299,18 +297,9 @@ public: enum Gradient { DetailsWidgetHeaderGradient, - Welcome_Button_GradientNormal, - Welcome_Button_GradientPressed }; enum ImageFile { - ProjectExplorerHeader, - ProjectExplorerSource, - ProjectExplorerForm, - ProjectExplorerResource, - ProjectExplorerQML, - ProjectExplorerOtherFiles, - ProjectFileIcon, IconOverlayCSource, IconOverlayCppHeader, IconOverlayCppSource, diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 191555b948b..33286ccd3d0 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -507,9 +507,12 @@ FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVers .arg(toolsPrefix(abi))); } -FileName AndroidConfig::gccPath(const Abi &abi, const QString &ndkToolChainVersion) const +FileName AndroidConfig::gccPath(const Abi &abi, ToolChain::Language lang, + const QString &ndkToolChainVersion) const { - return toolPath(abi, ndkToolChainVersion).appendString(QLatin1String("-gcc" QTC_HOST_EXE_SUFFIX)); + const QString tool + = HostOsInfo::withExecutableSuffix(QString::fromLatin1(lang == ToolChain::Language::C ? "-gcc" : "-g++")); + return toolPath(abi, ndkToolChainVersion).appendString(tool); } FileName AndroidConfig::gdbPath(const Abi &abi, const QString &ndkToolChainVersion) const @@ -1198,24 +1201,32 @@ QString AndroidConfigurations::defaultDevice(Project *project, const QString &ab return map.value(abi); } -static bool equalKits(Kit *a, Kit *b) +static bool matchToolChain(const ToolChain *atc, const ToolChain *btc) +{ + if (atc == btc) + return true; + + if (!atc || !btc) + return false; + + if (atc->typeId() != Constants::ANDROID_TOOLCHAIN_ID || btc->typeId() != Constants::ANDROID_TOOLCHAIN_ID) + return false; + + auto aatc = static_cast(atc); + auto abtc = static_cast(btc); + return aatc->ndkToolChainVersion() == abtc->ndkToolChainVersion() + && aatc->targetAbi() == abtc->targetAbi(); +} + +static bool matchKits(const Kit *a, const Kit *b) { if (QtSupport::QtKitInformation::qtVersion(a) != QtSupport::QtKitInformation::qtVersion(b)) return false; - ToolChain *atc = ToolChainKitInformation::toolChain(a, ToolChain::Language::Cxx); - ToolChain *btc = ToolChainKitInformation::toolChain(b, ToolChain::Language::Cxx); - if (atc == btc) - return true; - if (!atc || atc->typeId() != Constants::ANDROID_TOOLCHAIN_ID) - return false; - if (!btc || btc->typeId() != Constants::ANDROID_TOOLCHAIN_ID) - return false; - AndroidToolChain *aatc = static_cast(atc); - AndroidToolChain *bbtc = static_cast(btc); - if (aatc->ndkToolChainVersion() == bbtc->ndkToolChainVersion() - && aatc->targetAbi() == bbtc->targetAbi()) - return true; - return false; + + return matchToolChain(ToolChainKitInformation::toolChain(a, ToolChain::Language::Cxx), + ToolChainKitInformation::toolChain(b, ToolChain::Language::Cxx)) + && matchToolChain(ToolChainKitInformation::toolChain(a, ToolChain::Language::C), + ToolChainKitInformation::toolChain(b, ToolChain::Language::C)); } void AndroidConfigurations::registerNewToolChains() @@ -1243,32 +1254,13 @@ void AndroidConfigurations::removeOldToolChains() void AndroidConfigurations::updateAutomaticKitList() { - QList toolchains; - if (AndroidConfigurations::currentConfig().automaticKitCreation()) { - // having a empty toolchains list will remove all autodetected kits for android - // exactly what we want in that case - foreach (ToolChain *tc, ToolChainManager::toolChains()) { - if (!tc->isAutoDetected()) - continue; - if (tc->typeId() != Constants::ANDROID_TOOLCHAIN_ID) - continue; - if (!tc->isValid()) // going to be deleted - continue; - toolchains << static_cast(tc); - } - } + const QList existingKits = Utils::filtered(KitManager::kits(), [](const Kit *k) { + return k->isAutoDetected() && !k->isSdkProvided() + && DeviceTypeKitInformation::deviceTypeId(k) == Core::Id(Constants::ANDROID_DEVICE_TYPE); + }); - QList existingKits; - - foreach (Kit *k, KitManager::kits()) { - if (DeviceTypeKitInformation::deviceTypeId(k) != Core::Id(Constants::ANDROID_DEVICE_TYPE)) - continue; - if (!k->isAutoDetected()) - continue; - if (k->isSdkProvided()) - continue; - - // Update code for 3.0 beta, which shipped with a bug for the debugger settings + // Update code for 3.0 beta, which shipped with a bug for the debugger settings + for (Kit *k : existingKits) { ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx); if (tc && Debugger::DebuggerKitInformation::runnable(k).executable != tc->suggestedDebugger().toString()) { Debugger::DebuggerItem debugger; @@ -1281,14 +1273,15 @@ void AndroidConfigurations::updateAutomaticKitList() QVariant id = Debugger::DebuggerItemManager::registerDebugger(debugger); Debugger::DebuggerKitInformation::setDebugger(k, id); } - existingKits << k; } - QHash > qtVersionsForArch; - foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::unsortedVersions()) { - if (qtVersion->type() != QLatin1String(Constants::ANDROIDQT)) - continue; - QList qtAbis = qtVersion->qtAbis(); + QHash > qtVersionsForArch; + const QList qtVersions + = Utils::filtered(QtSupport::QtVersionManager::unsortedVersions(), [](const QtSupport::BaseQtVersion *v) { + return v->type() == Constants::ANDROIDQT; + }); + for (const QtSupport::BaseQtVersion *qtVersion : qtVersions) { + const QList qtAbis = qtVersion->qtAbis(); if (qtAbis.empty()) continue; qtVersionsForArch[qtAbis.first()].append(qtVersion); @@ -1298,22 +1291,36 @@ void AndroidConfigurations::updateAutomaticKitList() IDevice::ConstPtr device = dm->find(Core::Id(Constants::ANDROID_DEVICE_ID)); if (device.isNull()) { // no device, means no sdk path - foreach (Kit *k, existingKits) + for (Kit *k : existingKits) KitManager::deregisterKit(k); return; } // register new kits QList newKits; - foreach (AndroidToolChain *tc, toolchains) { - if (tc->isSecondaryToolChain()) + const QList tmp = Utils::filtered(ToolChainManager::toolChains(), [](ToolChain *tc) { + return tc->isAutoDetected() + && tc->isValid() + && tc->typeId() == Constants::ANDROID_TOOLCHAIN_ID + && !static_cast(tc)->isSecondaryToolChain(); + }); + const QList toolchains = Utils::transform(tmp, [](ToolChain *tc) { + return static_cast(tc); + }); + for (AndroidToolChain *tc : toolchains) { + if (tc->isSecondaryToolChain() || tc->language() != ToolChain::Language::Cxx) continue; - QList qtVersions = qtVersionsForArch.value(tc->targetAbi()); - foreach (QtSupport::BaseQtVersion *qt, qtVersions) { + const QList allLanguages = Utils::filtered(toolchains, + [tc](AndroidToolChain *otherTc) { + return tc->targetAbi() == otherTc->targetAbi(); + }); + for (const QtSupport::BaseQtVersion *qt : qtVersionsForArch.value(tc->targetAbi())) { Kit *newKit = new Kit; newKit->setAutoDetected(true); + newKit->setAutoDetectionSource("AndroidConfiguration"); DeviceTypeKitInformation::setDeviceTypeId(newKit, Core::Id(Constants::ANDROID_DEVICE_TYPE)); - ToolChainKitInformation::setToolChain(newKit, tc); + for (AndroidToolChain *tc : allLanguages) + ToolChainKitInformation::setToolChain(newKit, tc); QtSupport::QtKitInformation::setQtVersion(newKit, qt); DeviceKitInformation::setDevice(newKit, device); @@ -1329,48 +1336,25 @@ void AndroidConfigurations::updateAutomaticKitList() AndroidGdbServerKitInformation::setGdbSever(newKit, tc->suggestedGdbServer()); newKit->makeSticky(); + newKit->setUnexpandedDisplayName(tr("Android for %1 (GCC %2, Qt %3)") + .arg(static_cast(qt)->targetArch()) + .arg(tc->ndkToolChainVersion()) + .arg(qt->qtVersionString())); newKits << newKit; } } - for (int i = existingKits.count() - 1; i >= 0; --i) { - Kit *existingKit = existingKits.at(i); - for (int j = 0; j < newKits.count(); ++j) { - Kit *newKit = newKits.at(j); - if (equalKits(existingKit, newKit)) { - // Kit is already registered, nothing to do - newKits.removeAt(j); - existingKits.at(i)->makeSticky(); - existingKits.removeAt(i); - ToolChainKitInformation::setToolChain(existingKit, ToolChainKitInformation::toolChain(newKit, ToolChain::Language::Cxx)); - KitManager::deleteKit(newKit); - j = newKits.count(); - } - } - } - - foreach (Kit *k, existingKits) { - ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx); - QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k); - if (tc && tc->typeId() == Constants::ANDROID_TOOLCHAIN_ID - && tc->isValid() - && qtVersion && qtVersion->type() == QLatin1String(Constants::ANDROIDQT)) { - k->makeUnSticky(); - k->setAutoDetected(false); + QSet rediscoveredExistingKits; + for (Kit *newKit : newKits) { + Kit *existingKit = Utils::findOrDefault(existingKits, [newKit](const Kit *k) { return matchKits(newKit, k); }); + if (existingKit) { + existingKit->copyFrom(newKit); + KitManager::deleteKit(newKit); + rediscoveredExistingKits.insert(existingKit); } else { - KitManager::deregisterKit(k); + KitManager::registerKit(newKit); } } - - foreach (Kit *kit, newKits) { - AndroidToolChain *tc = static_cast(ToolChainKitInformation::toolChain(kit, ToolChain::Language::Cxx)); - AndroidQtVersion *qt = static_cast(QtSupport::QtKitInformation::qtVersion(kit)); - kit->setUnexpandedDisplayName(tr("Android for %1 (GCC %2, Qt %3)") - .arg(qt->targetArch()) - .arg(tc->ndkToolChainVersion()) - .arg(qt->qtVersionString())); - KitManager::registerKit(kit); - } } bool AndroidConfigurations::force32bitEmulator() diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 6a73c766951..1f6b2f6d07e 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -27,6 +27,8 @@ #include "android_global.h" +#include + #include #include #include @@ -126,7 +128,9 @@ public: Utils::FileName emulatorToolPath() const; - Utils::FileName gccPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const; + Utils::FileName gccPath(const ProjectExplorer::Abi &abi, + ProjectExplorer::ToolChain::Language lang, + const QString &ndkToolChainVersion) const; Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const; Utils::FileName keytoolPath() const; diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 50befd044fc..f5bafe43feb 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -314,6 +314,8 @@ void AndroidSettingsWidget::check(AndroidSettingsWidget::Mode mode) // Check for a gdb with a broken python QStringList gdbPaths; foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) { + if (ati.language == ProjectExplorer::ToolChain::Language::C) + continue; // we only check the arm gdbs, that's indicative enough if (ati.abi.architecture() != ProjectExplorer::Abi::ArmArchitecture) continue; @@ -329,8 +331,10 @@ void AndroidSettingsWidget::check(AndroidSettingsWidget::Mode mode) // See if we have qt versions for those toolchains QSet toolchainsForAbi; - foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) - toolchainsForAbi.insert(ati.abi); + foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) { + if (ati.language == ProjectExplorer::ToolChain::Language::Cxx) + toolchainsForAbi.insert(ati.abi); + } QSet qtVersionsForAbi; foreach (QtSupport::BaseQtVersion *qtVersion, QtSupport::QtVersionManager::unsortedVersions()) { @@ -496,16 +500,6 @@ void AndroidSettingsWidget::saveSettings() AndroidConfigurations::setConfig(m_androidConfig); } -int indexOf(const QList &list, const Utils::FileName &f) -{ - int end = list.count(); - for (int i = 0; i < end; ++i) { - if (list.at(i).compilerCommand == f) - return i; - } - return -1; -} - void AndroidSettingsWidget::sdkLocationEditingFinished() { m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath())); diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp index f89b6330715..6794d85d3e1 100644 --- a/src/plugins/android/androidtoolchain.cpp +++ b/src/plugins/android/androidtoolchain.cpp @@ -302,16 +302,17 @@ QList AndroidToolChainFact int idx = versionRegExp.indexIn(fileName); if (idx == -1) continue; - AndroidToolChainInformation ati; - ati.version = fileName.mid(idx + 1); - QString platform = fileName.left(idx); - ati.abi = AndroidConfig::abiForToolChainPrefix(platform); - if (ati.abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported - continue; - // AndroidToolChain *tc = new AndroidToolChain(arch, version, true); - ati.compilerCommand = AndroidConfigurations::currentConfig().gccPath(ati.abi, ati.version); - // tc->setCompilerCommand(compilerPath); - result.append(ati); + for (const ToolChain::Language lang : { ToolChain::Language::Cxx, ToolChain::Language::C }) { + AndroidToolChainInformation ati; + ati.language = lang; + ati.version = fileName.mid(idx + 1); + QString platform = fileName.left(idx); + ati.abi = AndroidConfig::abiForToolChainPrefix(platform); + if (ati.abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported + continue; + ati.compilerCommand = AndroidConfigurations::currentConfig().gccPath(ati.abi, lang, ati.version); + result.append(ati); + } } return result; } @@ -353,19 +354,22 @@ bool AndroidToolChainFactory::versionCompareLess(const QList &a, const QLis return false; } -bool AndroidToolChainFactory::versionCompareLess(AndroidToolChain *atc, AndroidToolChain *btc) +bool AndroidToolChainFactory::versionCompareLess(QList atc, + QList btc) { - QList a = versionNumberFromString(atc->ndkToolChainVersion()); - QList b = versionNumberFromString(btc->ndkToolChainVersion()); + const QList a = versionNumberFromString(atc.at(0)->ndkToolChainVersion()); + const QList b = versionNumberFromString(btc.at(0)->ndkToolChainVersion()); return versionCompareLess(a, b); } -static AndroidToolChain *findToolChain(Utils::FileName &compilerPath, const QList &alreadyKnown) +static AndroidToolChain *findToolChain(Utils::FileName &compilerPath, ToolChain::Language lang, + const QList &alreadyKnown) { return static_cast( - Utils::findOrDefault(alreadyKnown, [compilerPath](ToolChain *tc) { + Utils::findOrDefault(alreadyKnown, [compilerPath, lang](ToolChain *tc) { return tc->typeId() == Constants::ANDROID_TOOLCHAIN_ID + && tc->language() == lang && tc->compilerCommand() == compilerPath; })); } @@ -382,7 +386,7 @@ AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath, FileName path = ndkPath; QDirIterator it(path.appendPath(QLatin1String("toolchains")).toString(), QStringList() << QLatin1String("*"), QDir::Dirs); - QHash newestToolChainForArch; + QHash> newestToolChainForArch; while (it.hasNext()) { const QString &fileName = FileName::fromString(it.next()).fileName(); @@ -394,26 +398,30 @@ AndroidToolChainFactory::autodetectToolChainsForNdk(const FileName &ndkPath, Abi abi = AndroidConfig::abiForToolChainPrefix(platform); if (abi.architecture() == Abi::UnknownArchitecture) // e.g. mipsel which is not yet supported continue; - FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, version); + QList toolChainBundle; + for (ToolChain::Language lang : { ToolChain::Language::Cxx, ToolChain::Language::C }) { + FileName compilerPath = AndroidConfigurations::currentConfig().gccPath(abi, lang, version); - AndroidToolChain *tc = findToolChain(compilerPath, alreadyKnown); - if (!tc) { - tc = new AndroidToolChain(abi, version, ToolChain::Language::Cxx, - ToolChain::AutoDetection); - tc->resetToolChain(compilerPath); + AndroidToolChain *tc = findToolChain(compilerPath, lang, alreadyKnown); + if (!tc) { + tc = new AndroidToolChain(abi, version, lang, + ToolChain::AutoDetection); + tc->resetToolChain(compilerPath); + } + result.append(tc); + toolChainBundle.append(tc); } - result.append(tc); auto it = newestToolChainForArch.constFind(abi); if (it == newestToolChainForArch.constEnd()) - newestToolChainForArch.insert(abi, tc); - else if (versionCompareLess(it.value(), tc)) - newestToolChainForArch[abi] = tc; + newestToolChainForArch.insert(abi, toolChainBundle); + else if (versionCompareLess(it.value(), toolChainBundle)) + newestToolChainForArch[abi] = toolChainBundle; } foreach (ToolChain *tc, result) { AndroidToolChain *atc = static_cast(tc); - atc->setSecondaryToolChain(newestToolChainForArch.value(atc->targetAbi()) != atc); + atc->setSecondaryToolChain(!newestToolChainForArch.value(atc->targetAbi()).contains(atc)); } return result; diff --git a/src/plugins/android/androidtoolchain.h b/src/plugins/android/androidtoolchain.h index 5fd58532f2e..fecfe070fcf 100644 --- a/src/plugins/android/androidtoolchain.h +++ b/src/plugins/android/androidtoolchain.h @@ -104,6 +104,7 @@ public: class AndroidToolChainInformation { public: + ProjectExplorer::ToolChain::Language language; Utils::FileName compilerCommand; ProjectExplorer::Abi abi; QString version; @@ -116,7 +117,8 @@ public: static QList versionNumberFromString(const QString &version); static bool versionCompareLess(const QList &a, const QList &b); - static bool versionCompareLess(AndroidToolChain *atc, AndroidToolChain *btc); + static bool versionCompareLess(QList atc, + QList btc); static QList newestToolChainVersionForArch(const ProjectExplorer::Abi &abi); private: static QHash > m_newestVersionForAbi; diff --git a/src/plugins/appmanager/project/appmanagerproject.cpp b/src/plugins/appmanager/project/appmanagerproject.cpp index 9f79be2e63f..d0ea2824d41 100644 --- a/src/plugins/appmanager/project/appmanagerproject.cpp +++ b/src/plugins/appmanager/project/appmanagerproject.cpp @@ -129,6 +129,8 @@ void AppManagerProject::populateProject() foreach (ProjectExplorer::Target *target, targets()) targetUpdateDeployableFiles(target, files); } + + emit parsingFinished(); } void AppManagerProject::recursiveScanDirectory(const QDir &dir, QSet &container) diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index b66b37c1067..d07d3552343 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -231,6 +231,8 @@ void AutotoolsProject::makefileParsingFinished() m_makefileParserThread->deleteLater(); m_makefileParserThread = 0; + + emit parsingFinished(); } void AutotoolsProject::onFileChanged(const QString &file) diff --git a/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp b/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp index 462e26fe836..e6ae1404be2 100644 --- a/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp +++ b/src/plugins/clangstaticanalyzer/clangstaticanalyzerpreconfiguredsessiontests.cpp @@ -29,7 +29,6 @@ #include "clangstaticanalyzertool.h" #include "clangstaticanalyzerutils.h" -#include #include #include #include @@ -44,6 +43,7 @@ #include #include #include +#include #include @@ -66,6 +66,35 @@ static bool processEventsUntil(const std::function condition, int timeOu } } +class WaitForParsedProjects : public QObject +{ +public: + WaitForParsedProjects(ProjectExplorer::SessionManager &sessionManager, + const QStringList &projects) + : m_sessionManager(sessionManager) + , m_projectsToWaitFor(projects) + { + connect(&m_sessionManager, &ProjectExplorer::SessionManager::projectFinishedParsing, + this, &WaitForParsedProjects::onProjectFinishedParsing); + } + + void onProjectFinishedParsing(ProjectExplorer::Project *project) + { + m_projectsToWaitFor.removeOne(project->projectFilePath().toString()); + } + + bool wait() + { + return processEventsUntil([this]() { + return m_projectsToWaitFor.isEmpty(); + }); + } + +private: + ProjectExplorer::SessionManager &m_sessionManager; + QStringList m_projectsToWaitFor; +}; + namespace ClangStaticAnalyzer { namespace Internal { @@ -84,16 +113,14 @@ void ClangStaticAnalyzerPreconfiguredSessionTests::initTestCase() if (!m_sessionManager.sessions().contains(preconfiguredSessionName)) QSKIP("Manually preconfigured session 'ClangStaticAnalyzerPreconfiguredSession' needed."); - // Load session - if (m_sessionManager.activeSession() != preconfiguredSessionName) - QVERIFY(m_sessionManager.loadSession(preconfiguredSessionName)); + if (m_sessionManager.activeSession() == preconfiguredSessionName) + QSKIP("Session must not be already active."); - // Wait until all projects are loaded. - const int sessionManagerProjects = m_sessionManager.projects().size(); - const auto allProjectsLoaded = [sessionManagerProjects]() { - return CppModelManager::instance()->projectInfos().size() == sessionManagerProjects; - }; - QVERIFY(processEventsUntil(allProjectsLoaded)); + // Load session + const QStringList projects = m_sessionManager.projectsForSessionName(preconfiguredSessionName); + WaitForParsedProjects waitForParsedProjects(m_sessionManager, projects); + QVERIFY(m_sessionManager.loadSession(preconfiguredSessionName)); + QVERIFY(waitForParsedProjects.wait()); } void ClangStaticAnalyzerPreconfiguredSessionTests::testPreconfiguredSession() @@ -201,15 +228,15 @@ bool ClangStaticAnalyzerPreconfiguredSessionTests::switchToProjectAndTarget(Proj m_sessionManager.setStartupProject(project); if (target != project->activeTarget()) { - QSignalSpy waitUntilProjectUpdated(CppModelManager::instance(), - &CppModelManager::projectPartsUpdated); + QSignalSpy spyFinishedParsing(ProjectExplorer::SessionManager::instance(), + &ProjectExplorer::SessionManager::projectFinishedParsing); m_sessionManager.setActiveTarget(project, target, ProjectExplorer::SetActive::NoCascade); + QTC_ASSERT(spyFinishedParsing.wait(30000), return false); - const bool waitResult = waitUntilProjectUpdated.wait(30000); - if (!waitResult) { - qWarning() << "waitUntilProjectUpdated() failed"; - return false; - } + const QVariant projectArgument = spyFinishedParsing.takeFirst().takeFirst(); + QTC_ASSERT(projectArgument.canConvert(), return false); + + return projectArgument.value() == project; } return true; diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 8038890ef0e..0c266fbf6b6 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -145,6 +145,8 @@ void CMakeProject::updateProjectData() emit fileListChanged(); emit cmakeBc->emitBuildTypeChanged(); + + emit parsingFinished(); } void CMakeProject::updateQmlJSCodeModel() diff --git a/src/plugins/cmakeprojectmanager/configmodel.cpp b/src/plugins/cmakeprojectmanager/configmodel.cpp index 4ef34dd7fab..8516e3bc00b 100644 --- a/src/plugins/cmakeprojectmanager/configmodel.cpp +++ b/src/plugins/cmakeprojectmanager/configmodel.cpp @@ -44,17 +44,12 @@ ConfigModel::ConfigModel(QObject *parent) : QAbstractTableModel(parent) int ConfigModel::rowCount(const QModelIndex &parent) const { - QTC_ASSERT(parent.model() == nullptr || parent.model() == this, return 0); - if (parent.isValid()) - return 0; - return m_configuration.count(); + return parent.isValid() ? 0 : m_configuration.count(); } int ConfigModel::columnCount(const QModelIndex &parent) const { - QTC_ASSERT(!parent.isValid(), return 0); - QTC_ASSERT(parent.model() == nullptr, return 0); - return 3; + return parent.isValid() ? 0 : 3; } Qt::ItemFlags ConfigModel::flags(const QModelIndex &index) const diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 94ea044f3eb..63bdabb73ce 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -340,9 +340,8 @@ QIcon ManhattanStyle::standardIcon(StandardPixmap standardIcon, const QStyleOpti if (standardIcon == QStyle::SP_ComputerIcon) { // Ubuntu has in some versions a 16x16 icon, see QTCREATORBUG-12832 const QList &sizes = icon.availableSizes(); - if (Utils::allOf(sizes, [](const QSize &size) { return size.width() < 32;})) { - icon = QIcon(QLatin1String(":/core/images/Desktop.png")); - } + if (Utils::allOf(sizes, [](const QSize &size) { return size.width() < 32;})) + icon = QIcon(":/utils/images/Desktop.png"); } return icon; } diff --git a/src/plugins/coreplugin/systemsettings.ui b/src/plugins/coreplugin/systemsettings.ui index 0afda779ea0..b2928f9d47c 100644 --- a/src/plugins/coreplugin/systemsettings.ui +++ b/src/plugins/coreplugin/systemsettings.ui @@ -23,8 +23,8 @@ ? - - :/core/images/help.png:/core/images/help.png + + :/utils/images/help.png:/utils/images/help.png @@ -390,7 +390,7 @@ bigFilesLimitSpinBox - + diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp index 7d51e66414e..3434f87b72d 100644 --- a/src/plugins/cppeditor/cppquickfix_test.cpp +++ b/src/plugins/cppeditor/cppquickfix_test.cpp @@ -771,6 +771,66 @@ void CppEditorPlugin::test_quickfix_data() "}\n" ); + // Checks: No special treatment for reference to non const. + QTest::newRow("GenerateGetterSetter_referenceToNonConst") + << CppQuickFixFactoryPtr(new GenerateGetterSetter) << _( + "\n" + "class Something\n" + "{\n" + " int &it@;\n" + "};\n" + ) << _( + "\n" + "class Something\n" + "{\n" + " int ⁢\n" + "\n" + "public:\n" + " int &getIt() const;\n" + " void setIt(const int &value);\n" + "};\n" + "\n" + "int &Something::getIt() const\n" + "{\n" + " return it;\n" + "}\n" + "\n" + "void Something::setIt(const int &value)\n" + "{\n" + " it = value;\n" + "}\n" + ); + + // Checks: No special treatment for reference to const. + QTest::newRow("GenerateGetterSetter_referenceToConst") + << CppQuickFixFactoryPtr(new GenerateGetterSetter) << _( + "\n" + "class Something\n" + "{\n" + " const int &it@;\n" + "};\n" + ) << _( + "\n" + "class Something\n" + "{\n" + " const int ⁢\n" + "\n" + "public:\n" + " const int &getIt() const;\n" + " void setIt(const int &value);\n" + "};\n" + "\n" + "const int &Something::getIt() const\n" + "{\n" + " return it;\n" + "}\n" + "\n" + "void Something::setIt(const int &value)\n" + "{\n" + " it = value;\n" + "}\n" + ); + // Checks: // 1. Setter: Setter is a static function. // 2. Getter: Getter is a static, non const function. diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp index b7923d7217e..6fd02fd873a 100644 --- a/src/plugins/cppeditor/cppquickfixes.cpp +++ b/src/plugins/cppeditor/cppquickfixes.cpp @@ -3018,10 +3018,12 @@ public: if (passByValue) { paramString = oo.prettyType(fullySpecifiedType, paramName); } else { - FullySpecifiedType constParamType(fullySpecifiedType); + const ReferenceType *refType = type->asReferenceType(); + FullySpecifiedType constParamType(refType ? refType->elementType() + : fullySpecifiedType); constParamType.setConst(true); QScopedPointer referenceType(new ReferenceType(constParamType, false)); - FullySpecifiedType referenceToConstParamType(referenceType.data()); + const FullySpecifiedType referenceToConstParamType(referenceType.data()); paramString = oo.prettyType(referenceToConstParamType, paramName); } diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index 8ccd1793377..e33d20dd943 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -286,6 +286,7 @@ void GenericProject::refresh(RefreshOptions options) } refreshCppCodeModel(); + emit parsingFinished(); } /** diff --git a/src/plugins/ios/iosanalyzesupport.cpp b/src/plugins/ios/iosanalyzesupport.cpp index f44e6627b12..7f04ece41c8 100644 --- a/src/plugins/ios/iosanalyzesupport.cpp +++ b/src/plugins/ios/iosanalyzesupport.cpp @@ -102,8 +102,10 @@ void IosAnalyzeSupport::handleRemoteOutput(const QString &output) void IosAnalyzeSupport::handleRemoteErrorOutput(const QString &output) { - if (m_runControl) + if (m_runControl) { m_runControl->appendMessage(output, Utils::StdErrFormat); + m_outputParser.processOutput(output); + } } } // namespace Internal diff --git a/src/plugins/ios/simulatorcontrol.cpp b/src/plugins/ios/simulatorcontrol.cpp index a2807996a46..b300e77e6d5 100644 --- a/src/plugins/ios/simulatorcontrol.cpp +++ b/src/plugins/ios/simulatorcontrol.cpp @@ -64,6 +64,16 @@ static bool checkForTimeout(const std::chrono::time_point< std::chrono::high_res return timedOut; } +static QByteArray runSimCtlCommand(QStringList args) +{ + QProcess simCtlProcess; + args.prepend(QStringLiteral("simctl")); + simCtlProcess.start(QStringLiteral("xcrun"), args, QProcess::ReadOnly); + if (!simCtlProcess.waitForFinished()) + qCDebug(simulatorLog) << "simctl command failed." << simCtlProcess.errorString(); + return simCtlProcess.readAll(); +} + class SimulatorControlPrivate :QObject { Q_OBJECT private: @@ -79,7 +89,6 @@ private: SimulatorControlPrivate(QObject *parent = nullptr); ~SimulatorControlPrivate(); - QByteArray runSimCtlCommand(QStringList args) const; SimDeviceInfo deviceInfo(const QString &simUdid) const; bool runCommand(QString command, const QStringList &args, QByteArray *output = nullptr); @@ -105,7 +114,7 @@ QList SimulatorControl::availableSimulators() void SimulatorControl::updateAvailableSimulators() { - const QByteArray output = d->runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), QLatin1String("devices")}); + const QByteArray output = runSimCtlCommand({QLatin1String("list"), QLatin1String("-j"), QLatin1String("devices")}); QJsonDocument doc = QJsonDocument::fromJson(output); if (!doc.isNull()) { QList availableDevices; @@ -185,7 +194,7 @@ bool SimulatorControl::installApp(const QString &simUdid, const Utils::FileName { bool installed = false; if (isSimulatorRunning(simUdid)) { - commandOutput = d->runSimCtlCommand(QStringList() << QStringLiteral("install") << simUdid << bundlePath.toString()); + commandOutput = runSimCtlCommand(QStringList() << QStringLiteral("install") << simUdid << bundlePath.toString()); installed = commandOutput.isEmpty(); } else { commandOutput = "Simulator device not running."; @@ -199,7 +208,7 @@ qint64 SimulatorControl::launchApp(const QString &simUdid, const QString &bundle pId = -1; if (!bundleIdentifier.isEmpty() && isSimulatorRunning(simUdid)) { const QStringList args({QStringLiteral("launch"), simUdid , bundleIdentifier}); - const QByteArray output = d->runSimCtlCommand(args); + const QByteArray output = runSimCtlCommand(args); const QByteArray pIdStr = output.trimmed().split(' ').last().trimmed(); bool validInt = false; pId = pIdStr.toLongLong(&validInt); @@ -264,16 +273,6 @@ SimulatorControlPrivate::~SimulatorControlPrivate() } -QByteArray SimulatorControlPrivate::runSimCtlCommand(QStringList args) const -{ - QProcess simCtlProcess; - args.prepend(QStringLiteral("simctl")); - simCtlProcess.start(QStringLiteral("xcrun"), args, QProcess::ReadOnly); - if (!simCtlProcess.waitForFinished()) - qCDebug(simulatorLog) << "simctl command failed." << simCtlProcess.errorString(); - return simCtlProcess.readAll(); -} - // The simctl spawns the process and returns the pId but the application process might not have started, at least in a state where you can interrupt it. // Use SimulatorControl::waitForProcessSpawn to be sure. QProcess *SimulatorControl::spawnAppProcess(const QString &simUdid, const Utils::FileName &bundlePath, qint64 &pId, bool waitForDebugger, const QStringList &extraArgs) @@ -282,7 +281,7 @@ QProcess *SimulatorControl::spawnAppProcess(const QString &simUdid, const Utils: if (isSimulatorRunning(simUdid)) { QString bundleId = bundleIdentifier(bundlePath); QString executableName = bundleExecutable(bundlePath); - QByteArray appPath = d->runSimCtlCommand(QStringList() << QStringLiteral("get_app_container") << simUdid << bundleId).trimmed(); + QByteArray appPath = runSimCtlCommand(QStringList() << QStringLiteral("get_app_container") << simUdid << bundleId).trimmed(); if (!appPath.isEmpty() && !executableName.isEmpty()) { // Spawn the app. The spawned app is started in suspended mode. appPath.append('/' + executableName.toLocal8Bit()); diff --git a/src/plugins/nim/project/nimproject.cpp b/src/plugins/nim/project/nimproject.cpp index ec08a9d14eb..2df8e663175 100644 --- a/src/plugins/nim/project/nimproject.cpp +++ b/src/plugins/nim/project/nimproject.cpp @@ -140,6 +140,8 @@ void NimProject::updateProject() rootProjectNode()->buildTree(fileNodes); emit fileListChanged(); + + emit parsingFinished(); } bool NimProject::supportsKit(Kit *k, QString *) const diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index aeb799ae027..fe49ff41292 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -304,9 +304,6 @@ void JsonFieldPage::Field::setIsCompleteExpando(const QVariant &v, const QString // LabelFieldData: // -------------------------------------------------------------------- -LabelField::LabelField() : m_wordWrap(false) -{ } - bool LabelField::parseData(const QVariant &data, QString *errorMessage) { if (data.type() != QVariant::Map) { @@ -343,9 +340,6 @@ QWidget *LabelField::createWidget(const QString &displayName, JsonFieldPage *pag // SpacerFieldData: // -------------------------------------------------------------------- -SpacerField::SpacerField() : m_factor(1) -{ } - bool SpacerField::parseData(const QVariant &data, QString *errorMessage) { if (data.isNull()) @@ -388,9 +382,6 @@ QWidget *SpacerField::createWidget(const QString &displayName, JsonFieldPage *pa // LineEditFieldData: // -------------------------------------------------------------------- -LineEditField::LineEditField() : m_isModified(false), m_isValidating(false) -{ } - bool LineEditField::parseData(const QVariant &data, QString *errorMessage) { if (data.isNull()) @@ -502,9 +493,6 @@ void LineEditField::initializeData(MacroExpander *expander) // -------------------------------------------------------------------- -TextEditField::TextEditField() : m_acceptRichText(false) -{ } - bool TextEditField::parseData(const QVariant &data, QString *errorMessage) { if (data.isNull()) @@ -570,9 +558,6 @@ void TextEditField::initializeData(MacroExpander *expander) // PathChooserFieldData: // -------------------------------------------------------------------- -PathChooserField::PathChooserField() : m_kind(PathChooser::ExistingDirectory) -{ } - bool PathChooserField::parseData(const QVariant &data, QString *errorMessage) { if (data.isNull()) @@ -668,11 +653,6 @@ void PathChooserField::initializeData(MacroExpander *expander) // CheckBoxFieldData: // -------------------------------------------------------------------- -CheckBoxField::CheckBoxField() : - m_checkedValue(QLatin1String("0")), - m_uncheckedValue(QLatin1String("1")) -{ } - bool CheckBoxField::parseData(const QVariant &data, QString *errorMessage) { if (data.isNull()) diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h index e02723a5ec6..13cab66ccfe 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h @@ -63,36 +63,28 @@ public: class LabelField : public JsonFieldPage::Field { -public: - LabelField(); - private: QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; bool parseData(const QVariant &data, QString *errorMessage) override; - bool m_wordWrap; + bool m_wordWrap = false; QString m_text; }; class SpacerField : public JsonFieldPage::Field { public: - SpacerField(); - bool suppressName() const override { return true; } private: bool parseData(const QVariant &data, QString *errorMessage) override; QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; - int m_factor; + int m_factor = 1; }; class LineEditField : public JsonFieldPage::Field { -public: - LineEditField(); - private: bool parseData(const QVariant &data, QString *errorMessage) override; QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; @@ -102,10 +94,10 @@ private: bool validate(Utils::MacroExpander *expander, QString *message) override; void initializeData(Utils::MacroExpander *expander) override; - bool m_isModified; - bool m_isValidating; - bool m_restoreLastHistoryItem; - bool m_isPassword; + bool m_isModified = false; + bool m_isValidating = false; + bool m_restoreLastHistoryItem = false; + bool m_isPassword = false; QString m_placeholderText; QString m_defaultText; QString m_disabledText; @@ -117,9 +109,6 @@ private: class TextEditField : public JsonFieldPage::Field { -public: - TextEditField(); - private: bool parseData(const QVariant &data, QString *errorMessage) override; QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; @@ -130,7 +119,7 @@ private: void initializeData(Utils::MacroExpander *expander) override; QString m_defaultText; - bool m_acceptRichText; + bool m_acceptRichText = false; QString m_disabledText; mutable QString m_currentText; @@ -138,9 +127,6 @@ private: class PathChooserField : public JsonFieldPage::Field { -public: - PathChooserField(); - private: bool parseData(const QVariant &data, QString *errorMessage) override; @@ -155,7 +141,7 @@ private: QString m_path; QString m_basePath; QString m_historyId; - Utils::PathChooser::Kind m_kind; + Utils::PathChooser::Kind m_kind = Utils::PathChooser::ExistingDirectory; QString m_currentPath; }; @@ -163,8 +149,6 @@ private: class CheckBoxField : public JsonFieldPage::Field { public: - CheckBoxField(); - bool suppressName() const override { return true; } private: @@ -177,8 +161,8 @@ private: bool validate(Utils::MacroExpander *expander, QString *message) override; void initializeData(Utils::MacroExpander *expander) override; - QString m_checkedValue; - QString m_uncheckedValue; + QString m_checkedValue = QString("0"); + QString m_uncheckedValue = QString("1"); QVariant m_checkedExpression; bool m_isModified = false; @@ -186,9 +170,6 @@ private: class ComboBoxField : public JsonFieldPage::Field { -public: - ComboBoxField() = default; - private: bool parseData(const QVariant &data, QString *errorMessage) override; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index c7e5de51962..fab0226c40b 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -573,7 +573,7 @@ MsvcToolChainFactory::MsvcToolChainFactory() QSet MsvcToolChainFactory::supportedLanguages() const { - return { ProjectExplorer::ToolChain::Language::Cxx }; + return { ToolChain::Language::C, ToolChain::Language::Cxx }; } bool MsvcToolChainFactory::checkForVisualStudioInstallation(const QString &vsName) @@ -617,24 +617,32 @@ QString MsvcToolChainFactory::vcVarsBatFor(const QString &basePath, MsvcToolChai return QString(); } -static ToolChain *findOrCreateToolChain(const QList &alreadyKnown, - const QString &name, const Abi &abi, - const QString &varsBat, const QString &varsBatArg, - ToolChain::Detection d = ToolChain::ManualDetection) +static QList findOrCreateToolChain( + const QList &alreadyKnown, + const QString &name, const Abi &abi, + const QString &varsBat, const QString &varsBatArg, + ToolChain::Detection d = ToolChain::ManualDetection) { - ToolChain *tc = Utils::findOrDefault(alreadyKnown, - [&varsBat, &varsBatArg, &abi](ToolChain *tc) -> bool { - if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID) - return false; - if (tc->targetAbi() != abi) - return false; - auto mtc = static_cast(tc); - return mtc->varsBat() == varsBat - && mtc->varsBatArg() == varsBatArg; - }); - if (!tc) - tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, ToolChain::Language::Cxx, d); - return tc; + QList res; + for (auto language: {ToolChain::Language::C, ToolChain::Language::Cxx}) { + ToolChain *tc = Utils::findOrDefault( + alreadyKnown, + [&varsBat, &varsBatArg, &abi, &language](ToolChain *tc) -> bool { + if (tc->typeId() != Constants::MSVC_TOOLCHAIN_TYPEID) + return false; + if (tc->targetAbi() != abi) + return false; + if (tc->language() != language) + return false; + auto mtc = static_cast(tc); + return mtc->varsBat() == varsBat + && mtc->varsBatArg() == varsBatArg; + }); + if (!tc) + tc = new MsvcToolChain(name, abi, varsBat, varsBatArg, language, d); + res << tc; + } + return res; } // Detect build tools introduced with MSVC2015 @@ -670,10 +678,11 @@ static void detectCppBuildTools(QList *list) const Entry &e = entries[i]; const Abi abi(e.architecture, Abi::WindowsOS, Abi::WindowsMsvc2015Flavor, e.format, e.wordSize); - list->append(new MsvcToolChain(name + QLatin1String(e.postFix), abi, - vcVarsBat, QLatin1String(e.varsBatArg), - ToolChain::Language::Cxx, - ToolChain::AutoDetection)); + for (auto language: {ToolChain::Language::C, ToolChain::Language::Cxx}) { + list->append(new MsvcToolChain(name + QLatin1String(e.postFix), abi, + vcVarsBat, QLatin1String(e.varsBatArg), + language, ToolChain::AutoDetection)); + } } } @@ -743,19 +752,18 @@ QList MsvcToolChainFactory::autoDetect(const QList &al continue; QList tmp; - tmp.append(findOrCreateToolChain(alreadyKnown, - generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::x86), - findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::x86, sdkKey), - fi.absoluteFilePath(), QLatin1String("/x86"), ToolChain::AutoDetection)); - // Add all platforms, cross-compiler is automatically selected by SetEnv.cmd if needed - tmp.append(findOrCreateToolChain(alreadyKnown, - generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::amd64), - findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::amd64, sdkKey), - fi.absoluteFilePath(), QLatin1String("/x64"), ToolChain::AutoDetection)); - tmp.append(findOrCreateToolChain(alreadyKnown, - generateDisplayName(name, MsvcToolChain::WindowsSDK, MsvcToolChain::ia64), - findAbiOfMsvc(MsvcToolChain::WindowsSDK, MsvcToolChain::ia64, sdkKey), - fi.absoluteFilePath(), QLatin1String("/ia64"), ToolChain::AutoDetection)); + const QVector > platforms = { + {MsvcToolChain::x86, "x86"}, + {MsvcToolChain::amd64, "x64"}, + {MsvcToolChain::ia64, "ia64"}, + }; + for (auto platform: platforms) { + tmp.append(findOrCreateToolChain( + alreadyKnown, + generateDisplayName(name, MsvcToolChain::WindowsSDK, platform.first), + findAbiOfMsvc(MsvcToolChain::WindowsSDK, platform.first, sdkKey), + fi.absoluteFilePath(), "/" + platform.second, ToolChain::AutoDetection)); + } // Make sure the default is front. if (folder == defaultSdkPath) results = tmp + results; @@ -786,14 +794,16 @@ QList MsvcToolChainFactory::autoDetect(const QList &al const int version = vsName.leftRef(dotPos).toInt(); const QString vcvarsAllbat = path + QLatin1String("/vcvarsall.bat"); if (QFileInfo(vcvarsAllbat).isFile()) { - QList platforms; // prioritized list + // prioritized list. // x86_arm was put before amd64_arm as a workaround for auto detected windows phone // toolchains. As soon as windows phone builds support x64 cross builds, this change // can be reverted. - platforms << MsvcToolChain::x86 << MsvcToolChain::amd64_x86 - << MsvcToolChain::amd64 << MsvcToolChain::x86_amd64 - << MsvcToolChain::arm << MsvcToolChain::x86_arm << MsvcToolChain::amd64_arm - << MsvcToolChain::ia64 << MsvcToolChain::x86_ia64; + const QVector platforms = { + MsvcToolChain::x86, MsvcToolChain::amd64_x86, + MsvcToolChain::amd64, MsvcToolChain::x86_amd64, + MsvcToolChain::arm, MsvcToolChain::x86_arm, MsvcToolChain::amd64_arm, + MsvcToolChain::ia64, MsvcToolChain::x86_ia64 + }; foreach (const MsvcToolChain::Platform &platform, platforms) { const bool toolchainInstalled = QFileInfo(vcVarsBatFor(path, platform)).isFile(); if (hostSupportsPlatform(platform) && toolchainInstalled) { diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index f7dbc037c75..7f636f8ad81 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -168,6 +168,9 @@ signals: void projectContextUpdated(); void projectLanguagesUpdated(); +signals: // for tests only + void parsingFinished(); + protected: virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage); virtual bool setupTarget(Target *t); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 19f8e60c098..76d472032db 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1689,6 +1689,9 @@ ProjectExplorerPlugin::OpenProjectResult ProjectExplorerPlugin::openProjects(con foundProjectManager = true; QString tmp; if (Project *pro = manager->openProject(filePath, &tmp)) { + QObject::connect(pro, &Project::parsingFinished, [pro]() { + emit SessionManager::instance()->projectFinishedParsing(pro); + }); QString restoreError; Project::RestoreResult restoreResult = pro->restoreSettings(&restoreError); if (restoreResult == Project::RestoreResult::Ok) { diff --git a/src/plugins/projectexplorer/session.h b/src/plugins/projectexplorer/session.h index 0680c6e40ea..0b8aebdc5cd 100644 --- a/src/plugins/projectexplorer/session.h +++ b/src/plugins/projectexplorer/session.h @@ -138,6 +138,9 @@ signals: void aboutToSaveSession(); void dependencyChanged(ProjectExplorer::Project *a, ProjectExplorer::Project *b); +signals: // for tests only + void projectFinishedParsing(ProjectExplorer::Project *project); + private: static void saveActiveMode(Core::Id mode); void clearProjectFileCache(); diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 3dbe7d90412..a09586b5e00 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -202,7 +202,9 @@ bool ToolChain::operator == (const ToolChain &tc) const return true; // We ignore displayname - return typeId() == tc.typeId() && isAutoDetected() == tc.isAutoDetected(); + return typeId() == tc.typeId() + && isAutoDetected() == tc.isAutoDetected() + && language() == tc.language(); } /*! diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp index 7d5ede86fde..a2453ad45b1 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.cpp +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -619,6 +619,8 @@ void PythonProject::refresh() return new PythonFileNode(FileName::fromString(f), displayName); }); rootProjectNode()->buildTree(fileNodes); + + emit parsingFinished(); } /** diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 8260547a71e..002fbdaba71 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -503,6 +503,7 @@ void QbsProject::handleQbsParsingDone(bool success) if (dataChanged) updateAfterParse(); emit projectParsingDone(success); + emit parsingFinished(); } void QbsProject::handleRuleExecutionDone() diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp index 79fb5786588..a97ecc68add 100644 --- a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp +++ b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp @@ -87,7 +87,8 @@ void QmakeKitInformation::setup(Kit *k) break; } } - ToolChainKitInformation::setToolChain(k, possibleTc); + if (possibleTc) + ToolChainKitInformation::setToolChain(k, possibleTc); } } diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index e194c73882e..878553e96b3 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -87,26 +87,25 @@ using namespace Utils; struct FileTypeDataStorage { FileType type; - Theme::ImageFile themeImage; const char *typeName; const char *icon; const char *addFileFilter; }; static const FileTypeDataStorage fileTypeDataStorage[] = { - { HeaderType, Theme::ProjectExplorerHeader, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Headers"), + { HeaderType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Headers"), ProjectExplorer::Constants::FILEOVERLAY_H, "*.h; *.hh; *.hpp; *.hxx;"}, - { SourceType, Theme::ProjectExplorerSource, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Sources"), + { SourceType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Sources"), ProjectExplorer::Constants::FILEOVERLAY_CPP, "*.c; *.cc; *.cpp; *.cp; *.cxx; *.c++;" }, - { FormType, Theme::ProjectExplorerForm, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Forms"), + { FormType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Forms"), Constants::FILEOVERLAY_UI, "*.ui;" }, - { StateChartType, Theme::ProjectExplorerForm, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "State charts"), + { StateChartType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "State charts"), ProjectExplorer::Constants::FILEOVERLAY_SCXML, "*.scxml;" }, - { ResourceType, Theme::ProjectExplorerResource, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Resources"), + { ResourceType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Resources"), ProjectExplorer::Constants::FILEOVERLAY_QRC, "*.qrc;" }, - { QMLType, Theme::ProjectExplorerQML, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "QML"), + { QMLType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "QML"), ProjectExplorer::Constants::FILEOVERLAY_QML, "*.qml;" }, - { UnknownFileType, Theme::ProjectExplorerOtherFiles, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Other files"), + { UnknownFileType, QT_TRANSLATE_NOOP("QmakeProjectManager::QmakePriFileNode", "Other files"), ProjectExplorer::Constants::FILEOVERLAY_UNKNOWN, "*;" } }; @@ -159,10 +158,7 @@ QmakeNodeStaticData::QmakeNodeStaticData() const QPixmap dirPixmap = qApp->style()->standardIcon(QStyle::SP_DirIcon).pixmap(desiredSize); for (unsigned i = 0 ; i < count; ++i) { - QIcon overlayIcon; - const QString iconFile = creatorTheme()->imageFile(fileTypeDataStorage[i].themeImage, - QString::fromLatin1(fileTypeDataStorage[i].icon)); - overlayIcon = QIcon(iconFile); + const QIcon overlayIcon(QLatin1String(fileTypeDataStorage[i].icon)); QIcon folderIcon; folderIcon.addPixmap(FileIconProvider::overlayIcon(dirPixmap, overlayIcon)); const QString desc = QCoreApplication::translate("QmakeProjectManager::QmakePriFileNode", fileTypeDataStorage[i].typeName); @@ -171,9 +167,7 @@ QmakeNodeStaticData::QmakeNodeStaticData() desc, filter, folderIcon)); } // Project icon - const QString fileName = creatorTheme()->imageFile(Theme::ProjectFileIcon, - QLatin1String(ProjectExplorer::Constants::FILEOVERLAY_QT)); - const QIcon projectBaseIcon(fileName); + const QIcon projectBaseIcon(ProjectExplorer::Constants::FILEOVERLAY_QT); const QPixmap projectPixmap = FileIconProvider::overlayIcon(dirPixmap, projectBaseIcon); projectIcon.addPixmap(projectPixmap); @@ -205,7 +199,6 @@ public: QtSupport::ProFileReader *readerCumulative; ProFileGlobals *qmakeGlobals; QMakeVfs *qmakeVfs; - bool isQt5; }; class PriFileEvalResult @@ -244,7 +237,6 @@ public: TargetInformation targetInformation; InstallsList installsList; QHash newVarValues; - bool isDeployable; QStringList errors; }; @@ -645,7 +637,7 @@ PriFileEvalResult QmakePriFileNode::extractValues(const EvalInput &input, // all the files from those folders and add watchers for them. That's too // dangerous if we get the folders wrong and enumerate the whole project // tree multiple times. - QStringList dynamicVariables = dynamicVarNames(input.readerExact, input.isQt5); + QStringList dynamicVariables = dynamicVarNames(input.readerExact); foreach (ProFile *includeFileExact, includeFilesExact) foreach (const QString &dynamicVar, dynamicVariables) result.folders += input.readerExact->values(dynamicVar, includeFileExact); @@ -1467,25 +1459,15 @@ QStringList QmakePriFileNode::varNamesForRemoving() return vars; } -QStringList QmakePriFileNode::dynamicVarNames(QtSupport::ProFileReader *readerExact, - bool isQt5) +QStringList QmakePriFileNode::dynamicVarNames(QtSupport::ProFileReader *reader) { QStringList result; - // Figure out DEPLOYMENT and INSTALLS - const QString deployment = QLatin1String("DEPLOYMENT"); - const QString sources = QLatin1String(isQt5 ? ".files" : ".sources"); - QStringList listOfVars = readerExact->values(deployment); - foreach (const QString &var, listOfVars) { - result << (var + sources); - } - + // Figure out INSTALLS (and DEPLOYMENT, as it's aliased) const QString installs = QLatin1String("INSTALLS"); const QString files = QLatin1String(".files"); - listOfVars = readerExact->values(installs); - foreach (const QString &var, listOfVars) { + foreach (const QString &var, reader->values(installs)) result << (var + files); - } result.removeDuplicates(); return result; } @@ -1631,11 +1613,6 @@ QByteArray QmakeProFileNode::cxxDefines() const return result; } -bool QmakeProFileNode::isDeployable() const -{ - return m_isDeployable; -} - /*! \class QmakeProFileNode Implements abstract ProjectNode class @@ -1798,10 +1775,6 @@ EvalInput QmakeProFileNode::evalInput() const input.buildDirectory = buildDir(); input.readerExact = m_readerExact; input.readerCumulative = m_readerCumulative; - Target *t = m_project->activeTarget(); - Kit *k = t ? t->kit() : KitManager::defaultKit(); - QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(k); - input.isQt5 = !qtVersion || qtVersion->qtVersion() >= QtSupport::QtVersionNumber(5,0,0); input.qmakeGlobals = m_project->qmakeGlobals(); input.qmakeVfs = m_project->qmakeVfs(); return input; @@ -2012,19 +1985,6 @@ EvalResult *QmakeProFileNode::evaluate(const EvalInput &input) result->newVarValues[QmakeCc] = input.readerExact->values("QMAKE_CC"); result->newVarValues[QmakeCxx] = input.readerExact->values("QMAKE_CXX"); - result->isDeployable = false; - if (result->projectType == ApplicationTemplate) { - result->isDeployable = true; - } else { - foreach (const QString &item, input.readerExact->values(QLatin1String("DEPLOYMENT"))) { - if (!input.readerExact->values(item + QLatin1String(".sources")).isEmpty()) { - result->isDeployable = true; - break; - } - } - } - - if (readerBuildPass && readerBuildPass != input.readerExact) delete readerBuildPass; } @@ -2274,7 +2234,6 @@ void QmakeProFileNode::applyEvaluate(EvalResult *evalResult) m_subProjectsNotToDeploy = result->subProjectsNotToDeploy; m_installsList = result->installsList; - m_isDeployable = result->isDeployable; if (m_varValues != result->newVarValues) m_varValues = result->newVarValues; diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.h b/src/plugins/qmakeprojectmanager/qmakenodes.h index 1bb881f482a..32d3034470a 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.h +++ b/src/plugins/qmakeprojectmanager/qmakenodes.h @@ -180,7 +180,7 @@ protected: static QStringList varNames(ProjectExplorer::FileType type, QtSupport::ProFileReader *readerExact); static QStringList varNamesForRemoving(); static QString varNameForAdding(const QString &mimeType); - static QStringList dynamicVarNames(QtSupport::ProFileReader *readerExact, bool isQt5); + static QStringList dynamicVarNames(QtSupport::ProFileReader *readerExact); static QSet filterFilesProVariables(ProjectExplorer::FileType fileType, const QSet &files); static QSet filterFilesRecursiveEnumerata(ProjectExplorer::FileType fileType, const QSet &files); @@ -354,7 +354,6 @@ public: QString objectExtension() const; QString objectsDirectory() const; QByteArray cxxDefines() const; - bool isDeployable() const; enum AsyncUpdateDelay { ParseNow, ParseLater }; void scheduleUpdate(AsyncUpdateDelay delay); @@ -401,8 +400,6 @@ private: static TargetInformation targetInformation(QtSupport::ProFileReader *reader, QtSupport::ProFileReader *readerBuildPass, const QString &buildDir, const QString &projectFilePath); static InstallsList installsList(const QtSupport::ProFileReader *reader, const QString &projectFilePath, const QString &projectDir); - bool m_isDeployable = false; - bool m_validParse = false; bool m_parseInProgress = true; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index db652823cc1..11af8907631 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -753,6 +753,7 @@ void QmakeProject::decrementPendingEvaluateFutures() activeTarget()->updateDefaultDeployConfigurations(); updateRunConfigurations(); emit proFilesEvaluated(); + emit parsingFinished(); if (debug) qDebug()<<" Setting state to Base"; } diff --git a/src/plugins/qmldesigner/components/integration/componentview.cpp b/src/plugins/qmldesigner/components/integration/componentview.cpp index 1bd29179ec8..77d2bf78a8a 100644 --- a/src/plugins/qmldesigner/components/integration/componentview.cpp +++ b/src/plugins/qmldesigner/components/integration/componentview.cpp @@ -25,6 +25,9 @@ #include "componentview.h" #include "componentaction.h" + +#include + #include #include @@ -193,7 +196,9 @@ void ComponentView::searchForComponentAndAddToList(const ModelNode &node) foreach (const ModelNode &node, node.allSubModelNodesAndThisNode()) { if (node.nodeSourceType() == ModelNode::NodeWithComponentSource || (node.hasParentProperty() - && !node.parentProperty().isDefaultProperty())) { + && !node.parentProperty().isDefaultProperty() + && node.metaInfo().isValid() + && node.metaInfo().isGraphicalItem())) { if (masterNotAdded) { masterNotAdded = true; addMasterDocument(); diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 2614ca939ca..5b4da5cbd82 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -223,7 +223,7 @@ static bool idContainsWrongLetter(const QString& id) bool ModelNode::isValidId(const QString &id) { - return id.isEmpty() || (!idContainsWrongLetter(id) && !idIsQmlKeyWord(id)) && !isIdToAvoid(id); + return id.isEmpty() || (!idContainsWrongLetter(id) && !idIsQmlKeyWord(id) && !isIdToAvoid(id)); } bool ModelNode::hasId() const diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index b7d5552a8af..b8ab17a24df 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -198,6 +198,8 @@ void QmlProject::refresh(RefreshOptions options) QmlJS::Dialect::Qml); modelManager()->updateProjectInfo(projectInfo, this); + + emit parsingFinished(); } QStringList QmlProject::convertToAbsoluteFiles(const QStringList &paths) const diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp index 83a7256e6a9..ac2fd0d36ab 100644 --- a/src/plugins/qtsupport/exampleslistmodel.cpp +++ b/src/plugins/qtsupport/exampleslistmodel.cpp @@ -760,6 +760,14 @@ void ExamplesListModelFilter::updateFilter() } } +void ExamplesListModelFilter::setFilterStrings(const QStringList &arg) +{ + if (m_filterStrings != arg) { + m_filterStrings = arg; + delayedUpdateFilter(); + } +} + bool containsSubString(const QStringList &list, const QString &substr, Qt::CaseSensitivity cs) { return Utils::contains(list, [&substr, &cs](const QString &elem) { @@ -789,11 +797,11 @@ bool ExamplesListModelFilter::filterAcceptsRow(int sourceRow, const QModelIndex }); } - if (!m_searchString.isEmpty()) { + if (!m_filterStrings.isEmpty()) { const QString description = sourceModel()->index(sourceRow, 0, sourceParent).data(Description).toString(); const QString name = sourceModel()->index(sourceRow, 0, sourceParent).data(Name).toString(); - foreach (const QString &subString, m_searchString) { + foreach (const QString &subString, m_filterStrings) { bool wordMatch = false; wordMatch |= (bool)name.contains(subString, Qt::CaseInsensitive); if (wordMatch) @@ -835,6 +843,14 @@ void ExamplesListModelFilter::filterForExampleSet(int index) m_sourceModel->selectExampleSet(index); } +void ExamplesListModelFilter::setFilterTags(const QStringList &arg) +{ + if (m_filterTags != arg) { + m_filterTags = arg; + emit filterTagsChanged(arg); + } +} + void ExamplesListModelFilter::setShowTutorialsOnly(bool showTutorialsOnly) { m_showTutorialsOnly = showTutorialsOnly; @@ -984,8 +1000,13 @@ struct SearchStringLexer } }; -void ExamplesListModelFilter::parseSearchString(const QString &arg) +void ExamplesListModelFilter::setSearchString(const QString &arg) { + if (m_searchString == arg) + return; + m_searchString = arg; + emit searchStringChanged(m_searchString); + // parse and update QStringList tags; QStringList searchTerms; SearchStringLexer lex(arg); @@ -1007,10 +1028,15 @@ void ExamplesListModelFilter::parseSearchString(const QString &arg) } } - setSearchStrings(searchTerms); + setFilterStrings(searchTerms); setFilterTags(tags); delayedUpdateFilter(); } +QString ExamplesListModelFilter::searchString() const +{ + return m_searchString; +} + } // namespace Internal } // namespace QtSupport diff --git a/src/plugins/qtsupport/exampleslistmodel.h b/src/plugins/qtsupport/exampleslistmodel.h index cc1f9a735f0..522bd92624e 100644 --- a/src/plugins/qtsupport/exampleslistmodel.h +++ b/src/plugins/qtsupport/exampleslistmodel.h @@ -163,7 +163,7 @@ class ExamplesListModelFilter : public QSortFilterProxyModel public: Q_PROPERTY(bool showTutorialsOnly READ showTutorialsOnly WRITE setShowTutorialsOnly NOTIFY showTutorialsOnlyChanged) Q_PROPERTY(QStringList filterTags READ filterTags WRITE setFilterTags NOTIFY filterTagsChanged) - Q_PROPERTY(QStringList searchStrings READ searchStrings WRITE setSearchStrings NOTIFY searchStrings) + Q_PROPERTY(QString searchString READ searchString WRITE setSearchString NOTIFY searchStringChanged) Q_PROPERTY(int exampleSetIndex READ exampleSetIndex NOTIFY exampleSetIndexChanged) @@ -171,9 +171,11 @@ public: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + Q_INVOKABLE void setSearchString(const QString &arg); + QString searchString() const; + bool showTutorialsOnly() { return m_showTutorialsOnly; } QStringList filterTags() const { return m_filterTags; } - QStringList searchStrings() const { return m_searchString; } int rowCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; @@ -182,35 +184,21 @@ public: Q_INVOKABLE void filterForExampleSet(int index); public slots: - void setFilterTags(const QStringList &arg) - { - if (m_filterTags != arg) { - m_filterTags = arg; - emit filterTagsChanged(arg); - } - } + void setFilterTags(const QStringList &arg); void updateFilter(); - void setSearchStrings(const QStringList &arg) - { - if (m_searchString != arg) { - m_searchString = arg; - emit searchStrings(arg); - delayedUpdateFilter(); - } - } - - void parseSearchString(const QString &arg); void setShowTutorialsOnly(bool showTutorialsOnly); void handleQtVersionsChanged(); signals: void showTutorialsOnlyChanged(); void filterTagsChanged(const QStringList &arg); - void searchStrings(const QStringList &arg); + void searchStringChanged(const QString &arg); void exampleSetIndexChanged(); private: + void setFilterStrings(const QStringList &arg); + void qtVersionManagerLoaded(); void helpManagerInitialized(); @@ -221,8 +209,9 @@ private: int exampleSetIndex() const; bool m_showTutorialsOnly; + QString m_searchString; QStringList m_filterTags; - QStringList m_searchString; + QStringList m_filterStrings; ExamplesListModel *m_sourceModel; int m_timerId; bool m_blockIndexUpdate; diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index 97fc03e5fab..f18724a8b56 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -685,11 +685,6 @@ void QtOptionsPageWidget::userChangedCurrentVersion() updateDescriptionLabel(); } -void QtOptionsPageWidget::qtVersionChanged() -{ - updateDescriptionLabel(); -} - void QtOptionsPageWidget::updateDescriptionLabel() { QtVersionItem *item = currentItem(); @@ -736,7 +731,7 @@ void QtOptionsPageWidget::updateWidgets() m_versionUi->formLayout->addRow(m_configurationWidget); m_configurationWidget->setEnabled(!version->isAutodetected()); connect(m_configurationWidget, &QtConfigWidget::changed, - this, &QtOptionsPageWidget::qtVersionChanged); + this, &QtOptionsPageWidget::updateDescriptionLabel); } } else { m_versionUi->nameEdit->clear(); diff --git a/src/plugins/qtsupport/qtoptionspage.h b/src/plugins/qtsupport/qtoptionspage.h index 09363c89bc1..fc7899c9e03 100644 --- a/src/plugins/qtsupport/qtoptionspage.h +++ b/src/plugins/qtsupport/qtoptionspage.h @@ -85,7 +85,6 @@ private: private: void updateQtVersions(const QList &, const QList &, const QList &); - void qtVersionChanged(); void versionChanged(const QModelIndex ¤t, const QModelIndex &previous); void addQtDir(); void removeQtDir(); diff --git a/src/shared/qbs b/src/shared/qbs index 830503d0470..7a8a21c03b8 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 830503d04708ebd6a64dfd260b35cfeea26ba60a +Subproject commit 7a8a21c03b8e49e7dbf0462d2c698e960bdacbb4 diff --git a/src/tools/iostool/main.cpp b/src/tools/iostool/main.cpp index 76aa46efaba..0671cb258ce 100644 --- a/src/tools/iostool/main.cpp +++ b/src/tools/iostool/main.cpp @@ -103,7 +103,7 @@ class RelayServer: public QObject public: RelayServer(IosTool *parent); ~RelayServer(); - bool startServer(int port, bool ipv6); + bool startServer(int port); void stopServer(); quint16 serverPort(); IosTool *iosTool(); @@ -113,7 +113,8 @@ public: protected: virtual void newRelayConnection() = 0; - QTcpServer m_server; + QTcpServer m_ipv4Server; + QTcpServer m_ipv6Server; QList m_connections; }; @@ -188,7 +189,6 @@ private: int maxProgress; int opLeft; bool debug; - bool ipv6; bool inAppOutput; bool splitAppOutput; // as QXmlStreamReader reports the text attributes atomically it is better to split Ios::IosDeviceManager::AppOp appOp; @@ -404,31 +404,39 @@ RelayServer::~RelayServer() stopServer(); } -bool RelayServer::startServer(int port, bool ipv6) +bool RelayServer::startServer(int port) { - QTC_CHECK(!m_server.isListening()); - m_server.setMaxPendingConnections(1); - connect(&m_server, &QTcpServer::newConnection, this, &RelayServer::handleNewRelayConnection); + QTC_CHECK(!m_ipv4Server.isListening()); + QTC_CHECK(!m_ipv6Server.isListening()); + connect(&m_ipv4Server, &QTcpServer::newConnection, + this, &RelayServer::handleNewRelayConnection); + connect(&m_ipv6Server, &QTcpServer::newConnection, + this, &RelayServer::handleNewRelayConnection); quint16 portValue = static_cast(port); if (port < 0 || port > 0xFFFF) return false; - if (ipv6) - return m_server.listen(QHostAddress(QHostAddress::LocalHostIPv6), portValue); - else - return m_server.listen(QHostAddress(QHostAddress::LocalHost), portValue); + m_ipv4Server.listen(QHostAddress(QHostAddress::LocalHostIPv6), portValue); + m_ipv6Server.listen(QHostAddress(QHostAddress::LocalHost), portValue); + return m_ipv4Server.isListening() || m_ipv6Server.isListening(); } void RelayServer::stopServer() { foreach (Relayer *connection, m_connections) delete connection; - if (m_server.isListening()) - m_server.close(); + if (m_ipv4Server.isListening()) + m_ipv4Server.close(); + if (m_ipv6Server.isListening()) + m_ipv6Server.close(); } quint16 RelayServer::serverPort() { - return m_server.serverPort(); + if (m_ipv4Server.isListening()) + return m_ipv4Server.serverPort(); + if (m_ipv6Server.isListening()) + return m_ipv6Server.serverPort(); + return 0; } IosTool *RelayServer::iosTool() @@ -459,11 +467,12 @@ SingleRelayServer::SingleRelayServer(IosTool *parent, void SingleRelayServer::newRelayConnection() { + QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() + ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); if (m_connections.size() > 0) { - delete m_server.nextPendingConnection(); + delete clientSocket; return; } - QTcpSocket *clientSocket = m_server.nextPendingConnection(); if (clientSocket) { Relayer *newConnection = new Relayer(this, clientSocket); m_connections.append(newConnection); @@ -483,7 +492,8 @@ GenericRelayServer::GenericRelayServer(IosTool *parent, int remotePort, void GenericRelayServer::newRelayConnection() { - QTcpSocket *clientSocket = m_server.nextPendingConnection(); + QTcpSocket *clientSocket = m_ipv4Server.hasPendingConnections() + ? m_ipv4Server.nextPendingConnection() : m_ipv6Server.nextPendingConnection(); if (clientSocket) { iosTool()->errorMsg(QString::fromLatin1("setting up relayer for new connection")); RemotePortRelayer *newConnection = new RemotePortRelayer(this, clientSocket); @@ -498,7 +508,6 @@ IosTool::IosTool(QObject *parent): maxProgress(0), opLeft(0), debug(false), - ipv6(false), inAppOutput(false), splitAppOutput(true), appOp(Ios::IosDeviceManager::None), @@ -548,8 +557,6 @@ void IosTool::run(const QStringList &args) appOp = Ios::IosDeviceManager::AppOp(appOp | Ios::IosDeviceManager::Run); } else if (arg == QLatin1String("--noninteractive")) { // ignored for compatibility - } else if (arg == QLatin1String("--ipv6")) { - ipv6 = true; } else if (arg == QLatin1String("-v") || arg == QLatin1String("--verbose")) { echoRelays = true; } else if (arg == QLatin1String("-d") || arg == QLatin1String("--debug")) { @@ -721,12 +728,12 @@ void IosTool::didStartApp(const QString &bundlePath, const QString &deviceId, int qmlPort = deviceSession->qmljsDebugPort(); if (qmlPort) { qmlServer = new GenericRelayServer(this, qmlPort, deviceSession); - qmlServer->startServer(0, ipv6); + qmlServer->startServer(0); } } if (debug) { gdbServer = new SingleRelayServer(this, gdbFd); - if (!gdbServer->startServer(0, ipv6)) { + if (!gdbServer->startServer(0)) { doExit(-4); return; } diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index ee4e556094a..c048d893aba 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -1903,9 +1903,9 @@ void tst_Dumpers::dumper_data() + Check("h1.2.value.1", "[1]", "2", "int") + Check("h2", "<3 items>", "@QHash") - + Check("h2.0", "[0] 0", FloatValue("33"), "float") - + Check("h2.1", "[1] 22", FloatValue("22"), "float") - + Check("h2.2", "[2] 11", FloatValue("11"), "float") + + Check("h2.0", "[0] 0", FloatValue("33"), "") + + Check("h2.1", "[1] 22", FloatValue("22"), "") + + Check("h2.2", "[2] 11", FloatValue("11"), "") + Check("h3", "<1 items>", "@QHash<@QString, int>") + Check("h3.0.key", "key", "\"22.0\"", "@QString") @@ -1934,7 +1934,7 @@ void tst_Dumpers::dumper_data() + CheckType("h7.2.value", "@QPointer<@QObject>") + Check("h8", "<3 items>", "Hash") - + Check("h8.0", "[0] 22", FloatValue("22"), "float") + + Check("h8.0", "[0] 22", FloatValue("22"), "") + Check("it1.key", "22", "int") + Check("it1.value", FloatValue("22"), "float") + Check("it3.key", "33", "int") @@ -2347,8 +2347,8 @@ void tst_Dumpers::dumper_data() + Check("m1.1.value.0", "[0]", "\"22\"", "@QString") + Check("m2", "<2 items>", "@QMap") - + Check("m2.0", "[0] 11", FloatValue("31.0"), "float") - + Check("m2.1", "[1] 22", FloatValue("32.0"), "float") + + Check("m2.0", "[0] 11", FloatValue("31.0"), "") + + Check("m2.1", "[1] 22", FloatValue("32.0"), "") + Check("m3", "<2 items>", "T") @@ -2432,8 +2432,8 @@ void tst_Dumpers::dumper_data() + Check("m0", "<0 items>", "@QMultiMap") + Check("m1", "<6 items>", "@QMultiMap") - + Check("m1.0", "[0] 11", FloatValue("11"), "float") - + Check("m1.5", "[5] 22", FloatValue("22"), "float") + + Check("m1.0", "[0] 11", FloatValue("11"), "") + + Check("m1.5", "[5] 22", FloatValue("22"), "") + Check("m2", "<1 items>", "@QMultiMap<@QString, float>") + Check("m2.0.key", "\"22.0\"", "@QString") @@ -4120,23 +4120,23 @@ void tst_Dumpers::dumper_data() "map4.insert(std::pair(22, 25.0));\n") + Check("map1", "<2 items>", "std::map") - + Check("map1.0", "[0] 11", "1", "unsigned int") - + Check("map1.1", "[1] 22", "2", "unsigned int") + + Check("map1.0", "[0] 11", "1", "") + + Check("map1.1", "[1] 22", "2", "") + Check("map2", "<2 items>", "std::map") - + Check("map2.0", "[0] 11", FloatValue("11"), "float") - + Check("map2.1", "[1] 22", FloatValue("22"), "float") + + Check("map2.0", "[0] 11", FloatValue("11"), "") + + Check("map2.1", "[1] 22", FloatValue("22"), "") + Check("map3", "<6 items>", "Map") - + Check("map3.0", "[0] 11", FloatValue("11"), "float") + + Check("map3.0", "[0] 11", FloatValue("11"), "") + Check("it1.first", "11", "int") + Check("it1.second", FloatValue("11"), "float") + Check("it6.first", "66", "int") + Check("it6.second", FloatValue("66"), "float") + Check("map4", "<5 items>", "std::multimap") - + Check("map4.0", "[0] 11", FloatValue("11"), "float") - + Check("map4.4", "[4] 22", FloatValue("25"), "float"); + + Check("map4.0", "[0] 11", FloatValue("11"), "") + + Check("map4.4", "[4] 22", FloatValue("25"), ""); QTest::newRow("StdMapQt") @@ -4188,33 +4188,33 @@ void tst_Dumpers::dumper_data() + CoreProfile() + Check("map1", "<3 items>", "std::map<@QString, Foo>") - + Check("map1.0", "[0] \"22.0\"", "", "std::pair<@QString const, Foo>") + + Check("map1.0", "[0] \"22.0\"", "", "") + Check("map1.0.first", "\"22.0\"", "@QString") + Check("map1.0.second", "", "Foo") + Check("map1.0.second.a", "22", "int") - + Check("map1.1", "[1] \"33.0\"", "", "std::pair<@QString const, Foo>") + + Check("map1.1", "[1] \"33.0\"", "", "") + Check("map1.2.first", "\"44.0\"", "@QString") + Check("map1.2.second", "", "Foo") + Check("map1.2.second.a", "44", "int") + Check("map2", "<2 items>", "std::map") - + Check("map2.0", "[0] \"22.0\"", "", "std::pair") + + Check("map2.0", "[0] \"22.0\"", "", "") + Check("map2.0.first", "\"22.0\"", "char *") + Check("map2.0.first.0", "[0]", "50", "char") + Check("map2.0.second", "", "Foo") + Check("map2.0.second.a", "22", "int") - + Check("map2.1", "[1] \"33.0\"", "", "std::pair") + + Check("map2.1", "[1] \"33.0\"", "", "") + Check("map2.1.first", "\"33.0\"", "char *") + Check("map2.1.first.0", "[0]", "51", "char") + Check("map2.1.second", "", "Foo") + Check("map2.1.second.a", "33", "int") + Check("map3", "<2 items>", "std::map") - + Check("map3.0", "[0] 11", "<1 items>", "std::pair") + + Check("map3.0", "[0] 11", "<1 items>", "") + Check("map3.0.first", "11", "unsigned int") + Check("map3.0.second", "<1 items>", "@QStringList") + Check("map3.0.second.0", "[0]", "\"11\"", "@QString") - + Check("map3.1", "[1] 22", "<1 items>", "std::pair") + + Check("map3.1", "[1] 22", "<1 items>", "") + Check("map3.1.first", "22", "unsigned int") + Check("map3.1.second", "<1 items>", "@QStringList") + Check("map3.1.second.0", "[0]", "\"22\"", "@QString") @@ -4222,26 +4222,25 @@ void tst_Dumpers::dumper_data() + Check("map4.1.second.0", "[0]", "\"22\"", "@QString") + Check("map5", "<2 items>", "std::map<@QString, float>") - + Check("map5.0", "[0] \"11.0\"", FloatValue("11"), "std::pair<@QString const, float>") + + Check("map5.0", "[0] \"11.0\"", FloatValue("11"), "") + Check("map5.0.first", "\"11.0\"", "@QString") + Check("map5.0.second", FloatValue("11"), "float") - + Check("map5.1", "[1] \"22.0\"", FloatValue("22"), "std::pair<@QString const, float>") + + Check("map5.1", "[1] \"22.0\"", FloatValue("22"), "") + Check("map5.1.first", "\"22.0\"", "@QString") + Check("map5.1.second", FloatValue("22"), "float") + Check("map6", "<2 items>", "std::map") - + Check("map6.0", "[0] 11", "\"11.0\"", "std::pair") + + Check("map6.0", "[0] 11", "\"11.0\"", "") + Check("map6.0.first", "11", "int") + Check("map6.0.second", "\"11.0\"", "@QString") - + Check("map6.1", "[1] 22", "\"22.0\"", "std::pair") + + Check("map6.1", "[1] 22", "\"22.0\"", "") + Check("map6.1.first", "22", "int") + Check("map6.1.second", "\"22.0\"", "@QString") + Check("map7", "<3 items>", "std::map<@QString, @QPointer<@QObject>>") - + Check("map7.0", "[0] \".\"", "", "std::pair<@QString const, @QPointer<@QObject>>") + + Check("map7.0", "[0] \".\"", "", "") + Check("map7.0.first", "\".\"", "@QString") + Check("map7.0.second", "", "@QPointer<@QObject>") - + Check("map7.2", "[2] \"Welt\"", "", "std::pair<@QString const, @QPointer<@QObject>>") + Check("map7.2.first", "\"Welt\"", "@QString"); @@ -4672,8 +4671,8 @@ void tst_Dumpers::dumper_data() + Cxx11Profile() + Check("map1", "<2 items>", "std::unordered_map") - + Check("map1.0", "[0] 22", "2", "unsigned int") - + Check("map1.1", "[1] 11", "1", "unsigned int") + + Check("map1.0", "[0] 22", "2", "") + + Check("map1.1", "[1] 11", "1", "") + Check("map2", "<2 items>", "std::unordered_map") + Check("map2.0", "[0] \"22.0\"", FloatValue("22.0"), "") @@ -5382,17 +5381,8 @@ void tst_Dumpers::dumper_data() + Check("y2", "", "X") + Check("y3", "", "X"); - QTest::newRow("RValueReferenceLldb") + QTest::newRow("RValueReference") << Data(rvalueData) - + LldbEngine - + Check("x1", "", "X &&") - + Check("x2", "", "X &&") - + Check("x3", "", "X &&"); - - QTest::newRow("RValueReferenceGdb") - << Data(rvalueData) - + GdbEngine - + GccVersion(0, 40704) + Check("x1", "", "X &") + Check("x2", "", "X &") + Check("x3", "", "X &"); @@ -6231,6 +6221,7 @@ void tst_Dumpers::dumper_data() + Check("v15", "\"utf16\"", "@QJSValue (QString)") + Check("v15.1", "[1]", "116", "@QChar"); +#if 0 #ifdef Q_OS_LINUX // Hint: To open a failing test in Creator, do: // touch qt_tst_dumpers_Nim_.../dummy.nimproject @@ -6239,6 +6230,7 @@ void tst_Dumpers::dumper_data() nimData.configTest = "which nim"; nimData.allProfile = "CONFIG -= qt\n" + "SOURCES += main.nim\n" "# Prevents linking\n" "TARGET=\n" "# Overwrites qmake-generated 'all' target.\n" @@ -6253,8 +6245,10 @@ void tst_Dumpers::dumper_data() "proc mainProc =\n" " var name: string = \"Hello World\"\n" " var i: int = 43\n" + " var j: int\n" " var x: seq[int]\n" " x = @[1, 2, 3, 4, 5, 6]\n\n" + " j = i + name.len()\n" " # Crash it.\n" " var m1 = Mirror(tag:1)\n" " var m2 = Mirror(tag:2)\n" @@ -6278,6 +6272,7 @@ void tst_Dumpers::dumper_data() + Check("x", "<6 items>", Pattern("TY.*NI.6..")) // Something like "TY95019 (NI[6])" + Check("x.2", "[2]", "3", "NI"); #endif +#endif } int main(int argc, char *argv[]) diff --git a/tests/auto/runextensions/tst_runextensions.cpp b/tests/auto/runextensions/tst_runextensions.cpp index f1d77fa0605..acad21b15d2 100644 --- a/tests/auto/runextensions/tst_runextensions.cpp +++ b/tests/auto/runextensions/tst_runextensions.cpp @@ -44,6 +44,7 @@ private slots: void threadPriority(); void runAsyncNoFutureInterface(); void crefFunction(); + void onResultReady(); }; void report3(QFutureInterface &fi) @@ -557,6 +558,64 @@ void tst_RunExtensions::crefFunction() QCOMPARE(value, true); } +class ObjWithProperty : public QObject +{ + Q_OBJECT + +public slots: + void setValue(const QString &s) + { + value = s; + } + +public: + QString value; +}; + +void tst_RunExtensions::onResultReady() +{ + { // lambda + QFuture f = Utils::runAsync([](QFutureInterface &fi) { + fi.reportResult("Hi"); + fi.reportResult("there"); + }); + int count = 0; + QString res; + Utils::onResultReady(f, [&count, &res](const QString &s) { + ++count; + res = s; + }); + f.waitForFinished(); + QCoreApplication::processEvents(); + QCOMPARE(count, 2); + QCOMPARE(res, QString("there")); + } + { // lambda with guard + QFuture f = Utils::runAsync([](QFutureInterface &fi) { + fi.reportResult("Hi"); + fi.reportResult("there"); + }); + int count = 0; + ObjWithProperty obj; + Utils::onResultReady(f, &obj, [&count, &obj](const QString &s) { + ++count; + obj.setValue(s); + }); + f.waitForFinished(); + QCoreApplication::processEvents(); + QCOMPARE(count, 2); + QCOMPARE(obj.value, QString("there")); + } + { // member + QFuture f = Utils::runAsync([]() { return QString("Hi"); }); + ObjWithProperty obj; + Utils::onResultReady(f, &obj, &ObjWithProperty::setValue); + f.waitForFinished(); + QCoreApplication::processEvents(); + QCOMPARE(obj.value, QString("Hi")); + } +} + QTEST_MAIN(tst_RunExtensions) #include "tst_runextensions.moc"