{"id":400796,"date":"2024-06-29T15:28:26","date_gmt":"2024-06-29T15:28:26","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=400796"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=400796","title":{"rendered":"<span>Short-lived Music or MuseScore Code Analysis<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Having only programming background, it is impossible to develop software in some areas. Take the difficulties of medical software development as an example. The same is with music software, which will be discussed in this article. Here you need an advice of subject matter experts. However, it&#8217;s more expensive for software development. That is why developers sometimes save on code quality. The example of the MuseScore project check, described in the article, will show the importance of code quality expertise. Hopefully, programming and musical humor will brighten up the technical text.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/051\/319\/39d\/05131939d5d452b142d7ee78a4062eda.png\" width=\"400\" height=\"345\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/051\/319\/39d\/05131939d5d452b142d7ee78a4062eda.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>Introduction<\/h3>\n<p><a href=\"https:\/\/musescore.com\/\">MuseScore<\/a> is a computer program, a scorewriter for Windows, Mac OS X, and Linux operating systems. MuseScore allows you to quickly enter notes both with the computer keyboard and with an external MIDI keyboard. The scorewriter can import and export MIDI, MusicXML, LilyPond formats. It can also import MusE, Capella, and Band-in-a-Box. In addition, the program can export the scores to PDF, SVG, and PNG files, and to LilyPond for further fine-tuning.<\/p>\n<p>Previously, we <a href=\"https:\/\/www.viva64.com\/en\/b\/0530\/\">checked<\/a> the MuseScore code in 2017. It inspired us to write a series of 5 articles. There we reviewed the code of different programs for writing music.<\/p>\n<p>MuseScore is a really cool music platform. Fans of just finding popular melody notes will highly praise the program. Besides the desktop application, you can use the website or the mobile app. The download of ready-made notes has now become paid by subscription. However, it is usual for successful service development. Let&#8217;s hope, that the developers will allocate some of the earned money to improve code quality. Read on to find out why it&#8217;s time to pay attention to this.<\/p>\n<h3>Copy-paste code<\/h3>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/030\/aca\/de8\/030acade8345eafee18ec68f851b61c7.png\" width=\"580\" height=\"220\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/030\/aca\/de8\/030acade8345eafee18ec68f851b61c7.png\"\/><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions to the left and to the right of the &#8216;==&#8217; operator: desiredLen == desiredLen importmidi_simplify.cpp 44<\/p>\n<pre><code>bool areDurationsEqual(   const QList >&amp; durations,   const ReducedFraction&amp; desiredLen) {   ReducedFraction sum(0, 1);   for (const auto&amp; d: durations) {     sum += ReducedFraction(d.second.fraction()) \/ d.first;   }    return desiredLen == desiredLen; } <\/code><\/pre>\n<p>The comparison function for durations of notes (or some such) returns an incorrect result. All because of the copied <em>desiredLen<\/em> variable at the very end of the function. The correct code is most likely to look like this:<\/p>\n<pre><code>return desiredLen == sum; <\/code><\/pre>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions to the left and to the right of the &#8216;-&#8216; operator: i &#8212; i textbase.cpp 1986<\/p>\n<pre><code>void TextBase::layout1() {   ....   for (int i = 0; i &lt; rows(); ++i) {     TextBlock* t = &amp;_layout[i];     t->layout(this);     const QRectF* r = &amp;t->boundingRect();      if (r->height() == 0) {       r = &amp;_layout[i - i].boundingRect();    \/\/ &lt;=     }     y += t->lineSpacing();     t->setY(y);     bb |= r->translated(0.0, y);   }   .... } <\/code><\/pre>\n<p>The null element is always taken from the <em>layout<\/em> array because an error has slipped into the expression that calculates the index.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v523\/\">V523<\/a> The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. bsp.cpp 194<\/p>\n<pre><code>QString BspTree::debug(int index) const {   ....   if (node->type == Node::Type::HORIZONTAL) {     tmp += debug(firstChildIndex(index));     tmp += debug(firstChildIndex(index) + 1);   } else {     tmp += debug(firstChildIndex(index));     tmp += debug(firstChildIndex(index) + 1);   }   .... } <\/code><\/pre>\n<p>Code debugging is already a consequence of an earlier error in the code. Only errors in the debugging code can make the situation worse. Here the code of the two branches of the conditional operator is absolutely identical. No prizes for guessing that the code was copied to speed up the development. However, someone forgot to make changes to the second copy of the code.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v524\/\">V524<\/a> It is odd that the body of &#8216;downLine&#8217; function is fully equivalent to the body of &#8216;upLine&#8217; function. rest.cpp 718<\/p>\n<pre><code>int Rest::upLine() const {     qreal _spatium = spatium();     return lrint((pos().y() + bbox().top() + _spatium) * 2 \/ _spatium); }  int Rest::downLine() const {     qreal _spatium = spatium();     return lrint((pos().y() + bbox().top() + _spatium) * 2 \/ _spatium); } <\/code><\/pre>\n<p>The functions&#8217; names <em>upLine<\/em> and <em>downLine<\/em> reflect the opposite meaning. Though, this is not supported by the implementation of these functions. Most likely, there is another error caused by copying the code.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v778\/\">V778<\/a> Two similar code fragments were found. Perhaps, this is a typo and &#8216;description&#8217; variable should be used instead of &#8216;name&#8217;. instrumentsreader.cpp 407<\/p>\n<pre><code>void InstrumentsReader::fillByDeffault(Instrument&amp; instrument) const {   ....   if (instrument.name.isEmpty() &amp;&amp; !instrument.longNames.isEmpty()) {       instrument.name = instrument.longNames[0].name();   }   if (instrument.description.isEmpty() &amp;&amp; !instrument.longNames.isEmpty()) {       instrument.description = instrument.longNames[0].name();   }   .... } <\/code><\/pre>\n<p>Fields <em>instrument.name<\/em> and* instrument.description* are initialized with the same values. This makes the code suspicious. The names &#171;name&#187; and &#171;description&#187; are entities with quite different meanings. The index used to access the <em>longNames<\/em> array is most likely to differ here.<\/p>\n<h3>The new diagnostics&#8217; debut<\/h3>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c14\/564\/320\/c145643209c6b3c476bc73f2514f86ee.png\" width=\"400\" height=\"434\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c14\/564\/320\/c145643209c6b3c476bc73f2514f86ee.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Since the last review of this project, we have made some new diagnostics. They&#8217;ve helped us to find even more interesting errors.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v1063\/\">V1063<\/a> The modulo by 1 operation is meaningless. The result will always be zero. lyrics.h 85<\/p>\n<pre><code>class Lyrics final : public TextBase {   ....   bool isEven() const { return _no % 1; }   .... } <\/code><\/pre>\n<p>One of the new diagnostics found a very amusing error. The <em>isEven<\/em> function must return <em>true<\/em> if the number is even, otherwise, it must return <em>false<\/em> (odd). In fact, due to taking the remainder of 1, not 2, the function always returns the *false *value. That is, all numbers are considered odd.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v1065\/\">V1065<\/a> Expression can be simplified, check &#8216;1&#8217; and similar operands. scorediff.cpp 444<\/p>\n<pre><code>QString MscxModeDiff::getOuterLines(const QString&amp; str, int lines, bool start) {     lines = qAbs(lines);     const int secIdxStart = start ? 0 : (-1 - (lines - 1));     .... } <\/code><\/pre>\n<p>Perhaps, this is not an error. However, we can greatly simplify the code. So, here&#8217;s what it looks like:<\/p>\n<pre><code>const int secIdxStart = start ? 0 : -lines ; <\/code><\/pre>\n<p>On the other hand, the negative value as a position looks strange. <\/p>\n<h3>Pointers in C++: a timeless classic<\/h3>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/28a\/68b\/105\/28a68b105ba8500db826eae504612162.png\" width=\"400\" height=\"335\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/28a\/68b\/105\/28a68b105ba8500db826eae504612162.png\"\/><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v522\/\">V522<\/a> Dereferencing of the null pointer &#8216;family&#8217; might take place. instrtemplate.cpp 356<\/p>\n<pre><code>void InstrumentTemplate::write(XmlWriter&amp; xml) const {   ....   if (!family) {     xml.tag(\"family\", family->id);   }   xml.etag(); } <\/code><\/pre>\n<p>Inasmuch as the extra negation was written in the conditional expression, the added &#171;family&#187; tag can spell disaster.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v522\/\">V522<\/a> Dereferencing of the null pointer &#8216;destinationMeasure&#8217; might take place. score.cpp 4279<\/p>\n<pre><code>ChordRest* Score::cmdNextPrevSystem(ChordRest* cr, bool next) {   ....   auto destinationMeasure = currentSystem->firstMeasure();   ....   if (!(destinationMeasure = destinationMeasure->prevMeasure())) {     if (!(destinationMeasure = destinationMeasure->prevMeasureMM())) {         return cr;     }   }   .... } <\/code><\/pre>\n<p>This is a similar but less obvious situation. Here access to the <em>destinationMeasure<\/em> pointer in a nested conditional expression takes place. It&#8217;s dereferencing the null pointer.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v595\/\">V595<\/a> The &#8216;fd&#8217; pointer was utilized before it was verified against nullptr. Check lines: 5365, 5366. edit.cpp 5365<\/p>\n<pre><code>void Score::undoAddElement(Element* element) {   ....   FretDiagram* fd = toFretDiagram(ne);   Harmony* fdHarmony = fd->harmony();   if (fd) {     fdHarmony->setScore(score);     fdHarmony->setSelected(false);     fdHarmony->setTrack(staffIdx * VOICES + element->voice());   }   .... } <\/code><\/pre>\n<p>Fret Diagram (or FretBoard) is also used to record melodies \u2013 for instance, by guitarists. However, they are a bit out of luck. The error here is that the <em>fd<\/em> pointer is dereferenced before its validity is checked. The name of the function suggests that it happens when the addition of an element gets canceled. That is, the rollback of some changes in the notes can accidentally break the program. Thus, you&#8217;ll probably lose the notes.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v595\/\">V595<\/a> The &#8216;startSegment&#8217; pointer was utilized before it was verified against nullptr. Check lines: 129, 131. notationselectionrange.cpp 129<\/p>\n<pre><code>Ms::Segment* NotationSelectionRange::rangeStartSegment() const {   Ms::Segment* startSegment = score()->selection().startSegment();    startSegment->measure()->firstEnabled();  \/\/ &lt;=    if (!startSegment) {                      \/\/ &lt;=     return nullptr;   }    if (!startSegment->enabled()) {     startSegment = startSegment->next1MMenabled();   }   .... } <\/code><\/pre>\n<p>Unlike the previous code snippet, it seems to be failed refactoring. Most likely, the line dereferencing the <em>startSegment<\/em> pointer was added later. Moreover, it was displaced. It stands before the pointer validation.<\/p>\n<p>These were the most obvious warnings from this diagnostic. They were several lines apart from each other. Here&#8217;s a list of some other places that are worth viewing:<\/p>\n<ul>\n<li>\n<p>V595 The &#8216;note&#8217; pointer was utilized before it was verified against nullptr. Check lines: 5932, 5941. importmxmlpass2.cpp 5932<\/p>\n<\/li>\n<li>\n<p>V595 The &#8216;ed&#8217; pointer was utilized before it was verified against nullptr. Check lines: 599, 608. textedit.cpp 599<\/p>\n<\/li>\n<li>\n<p>V595 The &#8216;s&#8217; pointer was utilized before it was verified against nullptr. Check lines: 139, 143. elements.cpp 139<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v774\/\">V774<\/a> The &#8216;slur&#8217; pointer was used after the memory was released. importgtp-gp6.cpp 2592<\/p>\n<pre><code>void GuitarPro6::readGpif(QByteArray* data) {   ....   if (c) {     slur->setTick2(c->tick());     score->addElement(slur);     legatos[slur->track()] = 0;   } else {     delete slur;     legatos[slur->track()] = 0;   }   .... } <\/code><\/pre>\n<p>After the memory has been released, the data may still be in the same place for some time. So, no error will occur. However, you can&#8217;t rely on it. Besides, MuseScore is built for various platforms. This code may behave differently just after changing the compiler. In such a situation it&#8217;s better to swap the lines and correct a potential error. Also, it&#8217;s unclear why the memory is freed only in one branch of the code. <\/p>\n<h3>Miscellaneous warnings<\/h3>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/856\/98c\/37a\/85698c37ad416ced4cb33d61de77c31a.png\" width=\"400\" height=\"326\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/856\/98c\/37a\/85698c37ad416ced4cb33d61de77c31a.png\"\/><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v637\/\">V637<\/a> Two opposite conditions were encountered. The second condition is always false. Check lines: 4439, 4440. exportxml.cpp 4439<\/p>\n<pre><code>virtual Fraction tick() const override { return _tick; }  void ExportMusicXml::hairpin(....) {   ....   if (hp->tick() != tick) {         writeHairpinText(_xml, hp, hp->tick() == tick);   }   .... } <\/code><\/pre>\n<p>The <em>writeHairpinText<\/em> function call is likely to be simplified by passing the <em>false<\/em> value as the 3rd argument.<\/p>\n<p>The <em>tick<\/em> method is implemented like this:<\/p>\n<pre><code>virtual Fraction tick() const override { return _tick; } <\/code><\/pre>\n<p>It means, that there are no modifications of the class inside. So, the code can be slightly reduced without changing the program logic.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v763\/\">V763<\/a> Parameter &#8216;y&#8217; is always rewritten in function body before being used. tremolo.cpp 287<\/p>\n<pre><code>void Tremolo::layoutOneNoteTremolo(qreal x, qreal y, qreal spatium) {    bool up = chord()->up();   int line = up ? chord()->upLine() : chord()->downLine();   ....   qreal yLine = line + t;   ....   y = yLine * .5 * spatium;    setPos(x, y); } <\/code><\/pre>\n<p>The prototype of the function is a certain agreement between its user and the function&#8217;s author. The code always looks very suspicious if the function arguments are overwritten in the code without any conditions. As it happens here with the <em>y<\/em> variable&#8217;s value.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v506\/\">V506<\/a> Pointer to local variable &#8216;handle&#8217; is stored outside the scope of this variable. Such a pointer will become invalid. ove.cpp 4391<\/p>\n<pre><code>class BasicParse {   .... protected:   StreamHandle* m_handle;   .... }  bool OvscParse::parse() {   Block* dataBlock = m_chunk->getDataBlock();   unsigned int blockSize = m_chunk->getSizeBlock()->toSize();   StreamHandle handle(dataBlock->data(), blockSize);   Block placeHolder;    m_handle = &amp;handle;   .... } <\/code><\/pre>\n<p>The analyzer found several dangerous places. They might spoil all the fun when the pointer to a local object, created in one of the functions, is stored in a class field. Such a pointer can indicate garbage data in memory later.<\/p>\n<p>The analyzer found all such places in one file:<\/p>\n<ul>\n<li>\n<p>V506 Pointer to local variable &#8216;handle&#8217; is stored outside the scope of this variable. Such a pointer will become invalid. ove.cpp 4483<\/p>\n<\/li>\n<li>\n<p>V506 Pointer to local variable &#8216;handle&#8217; is stored outside the scope of this variable. Such a pointer will become invalid. ove.cpp 4930<\/p>\n<\/li>\n<li>\n<p>V506 Pointer to local variable &#8216;handle&#8217; is stored outside the scope of this variable. Such a pointer will become invalid. ove.cpp 9291<\/p>\n<\/li>\n<li>\n<p>V506 Pointer to local variable &#8216;handle&#8217; is stored outside the scope of this variable. Such a pointer will become invalid. ove.cpp 9507<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v519\/\">V519<\/a> The &#8216;savedExtension.status&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 349, 352. extensionsservice.cpp 352<\/p>\n<pre><code>void ExtensionsService::th_refreshExtensions() {   ....   if (savedExtension.version &lt; extension.version) {       savedExtension.status = ExtensionStatus::NeedUpdate;   }    savedExtension.status = ExtensionStatus::Installed;   .... } <\/code><\/pre>\n<p>It looks like some extension will never get an update. This is because of the error: the extension status is always overwritten with the <em>Installed<\/em> value.<\/p>\n<p>Here&#8217;s the entire list of similar places with variable values overwritten:<\/p>\n<ul>\n<li>\n<p>V519 The &#8216;lyrNote&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 962, 972. importgtp-gp6.cpp 972<\/p>\n<\/li>\n<li>\n<p>V519 The &#8216;_crossMeasure&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 2545, 2550. chord.cpp 2550<\/p>\n<\/li>\n<li>\n<p>V519 The &#8216;bt&#8217; variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 417, 418. chordrest.cpp 418<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v612\/\">V612<\/a> An unconditional &#8216;return&#8217; within a loop. noteinputbarmodel.cpp 371<\/p>\n<pre><code>int NoteInputBarModel::resolveCurrentVoiceIndex() const {   ....   for (const Element* element: selection()->elements()) {       return element->voice();   }   .... } <\/code><\/pre>\n<p>It is impossible to pass by a loop of one iteration without asking: &#171;Why?&#187;.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v1009\/\">V1009<\/a> Check the array initialization. Only the first element is initialized explicitly. The rest elements are initialized with zeros. instrumentstypes.h 135<\/p>\n<pre><code>static constexpr int MAX_STAVES  = 4;  enum class BracketType : signed char {     NORMAL, BRACE, SQUARE, LINE, NO_BRACKET = -1 };  struct Instrument {   ....   BracketType bracket[MAX_STAVES] = { BracketType::NO_BRACKET };   .... } <\/code><\/pre>\n<p>The author of the code thought that the <em>bracket<\/em> array is fully initialized with <em>NO_BRACKET<\/em> values. The numeric representation of this value is -1. According to the rules of such an initializer, only the first element is initialized with the specified value. All the others get the 0 value. It must be <em>NORMAL<\/em>, not <em>NO_BRACKET<\/em>. Most likely, such default values were not supposed to be ever read.<\/p>\n<h3>Open Source quality at large<\/h3>\n<p>In general, open source projects lack attention. Otherwise, we wouldn&#8217;t have done so many <a href=\"https:\/\/www.viva64.com\/en\/inspections\/\">error reviews<\/a> of different projects. Another problem, that outright spoils the quality of the code, is the migration of errors from project to project. The most famous case in our living memory is the code of the <a href=\"https:\/\/www.viva64.com\/en\/b\/0574\/\">Amazon Lumberyard<\/a> game engine. Here, the developers took the CryEngine code with errors as a basis. Moreover, the errors were fixed in the latest version of the original engine.<\/p>\n<p>MuseScore developers faced a similar problem. They used the <a href=\"https:\/\/github.com\/ekg\/intervaltree\">intervaltree<\/a> library in the project. There was the following mistake:<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v630\/\">V630<\/a> The &#8216;malloc&#8217; function is used to allocate memory for an array of objects which are classes containing constructors and destructors. IntervalTree.h 70<\/p>\n<pre><code>IntervalTree(const intervalTree&amp; other) {     center = other.center;     intervals = other.intervals;     if (other.left) {         left = (intervalTree*) malloc(sizeof(intervalTree));  \/\/ &lt;=         *left = *other.left;     } else {         left = NULL;     }     if (other.right) {         right = new intervalTree();         *right = *other.right;     } else {         right = NULL;     } }  IntervalTree&amp; operator=(const intervalTree&amp; other) {     center = other.center;     intervals = other.intervals;     if (other.left) {         left = new intervalTree();                            \/\/ &lt;=         *left = *other.left;     } else {         left = NULL;     }     if (other.right) {         right = new intervalTree();                           \/\/ &lt;=         *right = *other.right;     } else {         right = NULL;     }     return *this; } <\/code><\/pre>\n<p>The developers resorted to using the <em>malloc<\/em> function in one place. They did it to allocate memory for the class. Although, they used the <em>new<\/em> operator in all other cases. Certainly, the right option is to use <em>new<\/em>, the memory allocation operator (C++). It&#8217;s worth using since the <em>IntervalTree<\/em> class contains a constructor and a destructor.<\/p>\n<p>Let&#8217;s come back to the quality of open source projects in general. The code was rewritten 2 years ago. The error doesn&#8217;t exist anymore. Now it dwells only in numerous forks and other projects.<\/p>\n<p>Do you still remember the example from the article?<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v523\/\">V523<\/a> The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. bsp.cpp 194<\/p>\n<pre><code>QString BspTree::debug(int index) const {   ....   if (node->type == Node::Type::HORIZONTAL) {     tmp += debug(firstChildIndex(index));     tmp += debug(firstChildIndex(index) + 1);   } else {     tmp += debug(firstChildIndex(index));     tmp += debug(firstChildIndex(index) + 1);   }   .... } <\/code><\/pre>\n<p>Actually, it was copied from the <a href=\"https:\/\/github.com\/qt\/qtbase\">QtBase<\/a> code. Take a look at its full form:<\/p>\n<pre><code>QString QGraphicsSceneBspTree::debug(int index) const {     const Node *node = &amp;nodes.at(index);      QString tmp;     if (node->type == Node::Leaf) {         QRectF rect = rectForIndex(index);         if (!leaves[node->leafIndex].isEmpty()) {             tmp += QString::fromLatin1(\"[%1, %2, %3, %4] contains %5 items\\n\")                    .arg(rect.left()).arg(rect.top())                    .arg(rect.width()).arg(rect.height())                    .arg(leaves[node->leafIndex].size());         }     } else {         if (node->type == Node::Horizontal) {             tmp += debug(firstChildIndex(index));             tmp += debug(firstChildIndex(index) + 1);         } else {             tmp += debug(firstChildIndex(index));             tmp += debug(firstChildIndex(index) + 1);         }     }      return tmp; } <\/code><\/pre>\n<p>When this article was published, the code contained the error both in MuseScore and QtBase.<\/p>\n<h3>Conclusion<\/h3>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/487\/09a\/451\/48709a4513ffece3c3499943d74a739b.png\" width=\"400\" height=\"290\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/487\/09a\/451\/48709a4513ffece3c3499943d74a739b.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Nowadays, music software is quite a mass product. The modern media industry uses computer algorithms to edit music and audio recordings. However, for some reason, the industry has not yet created a culture of code quality control. <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio\/\">PVS-Studio<\/a>, our static analyzer, issued lots of warnings during open source programs checks. In this article, we described the errors found in programs designed to edit music. This indirectly confirms the lack of code quality control in the media industry. Once we reviewed the code of Steinberg SDK, the commercial library. Steinberg Media Technologies GmbH is a German music company that developed the library. Here, we also <a href=\"https:\/\/www.viva64.com\/en\/b\/0541\/\">found<\/a> a significant number of code defects.<\/p>\n<p>There are many game studios, banks, and IT giants among our <a href=\"https:\/\/www.viva64.com\/en\/customers\/\">customers<\/a>. However, we haven&#8217;t worked with top music industry companies so far. I hope, that the article will inspire the largest music companies to just use the PVS-Studio <a href=\"https:\/\/www.viva64.com\/en\/pvs-studio-download\/\">trial<\/a> on their projects. <\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/545624\/\"> https:\/\/habr.com\/ru\/articles\/545624\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>Having only programming background, it is impossible to develop software in some areas. Take the difficulties of medical software development as an example. The same is with music software, which will be discussed in this article. Here you need an advice of subject matter experts. However, it&#8217;s more expensive for software development. That is why developers sometimes save on code quality. The example of the MuseScore project check, described in the article, will show the importance of code quality expertise. Hopefully, programming and musical humor will brighten up the technical text.<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<h3>Introduction<\/h3>\n<p><a href=\"https:\/\/musescore.com\/\">MuseScore<\/a> is a computer program, a scorewriter for Windows, Mac OS X, and Linux operating systems. MuseScore allows you to quickly enter notes both with the computer keyboard and with an external MIDI keyboard. The scorewriter can import and export MIDI, MusicXML, LilyPond formats. It can also import MusE, Capella, and Band-in-a-Box. In addition, the program can export the scores to PDF, SVG, and PNG files, and to LilyPond for further fine-tuning.<\/p>\n<p>Previously, we <a href=\"https:\/\/www.viva64.com\/en\/b\/0530\/\">checked<\/a> the MuseScore code in 2017. It inspired us to write a series of 5 articles. There we reviewed the code of different programs for writing music.<\/p>\n<p>MuseScore is a really cool music platform. Fans of just finding popular melody notes will highly praise the program. Besides the desktop application, you can use the website or the mobile app. The download of ready-made notes has now become paid by subscription. However, it is usual for successful service development. Let&#8217;s hope, that the developers will allocate some of the earned money to improve code quality. Read on to find out why it&#8217;s time to pay attention to this.<\/p>\n<h3>Copy-paste code<\/h3>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions to the left and to the right of the &#8216;==&#8217; operator: desiredLen == desiredLen importmidi_simplify.cpp 44<\/p>\n<pre><code>bool areDurationsEqual(   const QList >&amp; durations,   const ReducedFraction&amp; desiredLen) {   ReducedFraction sum(0, 1);   for (const auto&amp; d: durations) {     sum += ReducedFraction(d.second.fraction()) \/ d.first;   }    return desiredLen == desiredLen; } <\/code><\/pre>\n<p>The comparison function for durations of notes (or some such) returns an incorrect result. All because of the copied <em>desiredLen<\/em> variable at the very end of the function. The correct code is most likely to look like this:<\/p>\n<pre><code>return desiredLen == sum; <\/code><\/pre>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v501\/\">V501<\/a> There are identical sub-expressions to the left and to the right of the &#8216;-&#8216; operator: i &#8212; i textbase.cpp 1986<\/p>\n<pre><code>void TextBase::layout1() {   ....   for (int i = 0; i &lt; rows(); ++i) {     TextBlock* t = &amp;_layout[i];     t->layout(this);     const QRectF* r = &amp;t->boundingRect();      if (r->height() == 0) {       r = &amp;_layout[i - i].boundingRect();    \/\/ &lt;=     }     y += t->lineSpacing();     t->setY(y);     bb |= r->translated(0.0, y);   }   .... } <\/code><\/pre>\n<p>The null element is always taken from the <em>layout<\/em> array because an error has slipped into the expression that calculates the index.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v523\/\">V523<\/a> The &#8216;then&#8217; statement is equivalent to the &#8216;else&#8217; statement. bsp.cpp 194<\/p>\n<pre><code>QString BspTree::debug(int index) const {   ....   if (node->type == Node::Type::HORIZONTAL) {     tmp += debug(firstChildIndex(index));     tmp += debug(firstChildIndex(index) + 1);   } else {     tmp += debug(firstChildIndex(index));     tmp += debug(firstChildIndex(index) + 1);   }   .... } <\/code><\/pre>\n<p>Code debugging is already a consequence of an earlier error in the code. Only errors in the debugging code can make the situation worse. Here the code of the two branches of the conditional operator is absolutely identical. No prizes for guessing that the code was copied to speed up the development. However, someone forgot to make changes to the second copy of the code.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v524\/\">V524<\/a> It is odd that the body of &#8216;downLine&#8217; function is fully equivalent to the body of &#8216;upLine&#8217; function. rest.cpp 718<\/p>\n<pre><code>int Rest::upLine() const {     qreal _spatium = spatium();     return lrint((pos().y() + bbox().top() + _spatium) * 2 \/ _spatium); }  int Rest::downLine() const {     qreal _spatium = spatium();     return lrint((pos().y() + bbox().top() + _spatium) * 2 \/ _spatium); } <\/code><\/pre>\n<p>The functions&#8217; names <em>upLine<\/em> and <em>downLine<\/em> reflect the opposite meaning. Though, this is not supported by the implementation of these functions. Most likely, there is another error caused by copying the code.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v778\/\">V778<\/a> Two similar code fragments were found. Perhaps, this is a typo and &#8216;description&#8217; variable should be used instead of &#8216;name&#8217;. instrumentsreader.cpp 407<\/p>\n<pre><code>void InstrumentsReader::fillByDeffault(Instrument&amp; instrument) const {   ....   if (instrument.name.isEmpty() &amp;&amp; !instrument.longNames.isEmpty()) {       instrument.name = instrument.longNames[0].name();   }   if (instrument.description.isEmpty() &amp;&amp; !instrument.longNames.isEmpty()) {       instrument.description = instrument.longNames[0].name();   }   .... } <\/code><\/pre>\n<p>Fields <em>instrument.name<\/em> and* instrument.description* are initialized with the same values. This makes the code suspicious. The names &#171;name&#187; and &#171;description&#187; are entities with quite different meanings. The index used to access the <em>longNames<\/em> array is most likely to differ here.<\/p>\n<h3>The new diagnostics&#8217; debut<\/h3>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>Since the last review of this project, we have made some new diagnostics. They&#8217;ve helped us to find even more interesting errors.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v1063\/\">V1063<\/a> The modulo by 1 operation is meaningless. The result will always be zero. lyrics.h 85<\/p>\n<pre><code>class Lyrics final : public TextBase {   ....   bool isEven() const { return _no % 1; }   .... } <\/code><\/pre>\n<p>One of the new diagnostics found a very amusing error. The <em>isEven<\/em> function must return <em>true<\/em> if the number is even, otherwise, it must return <em>false<\/em> (odd). In fact, due to taking the remainder of 1, not 2, the function always returns the *false *value. That is, all numbers are considered odd.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v1065\/\">V1065<\/a> Expression can be simplified, check &#8216;1&#8217; and similar operands. scorediff.cpp 444<\/p>\n<pre><code>QString MscxModeDiff::getOuterLines(const QString&amp; str, int lines, bool start) {     lines = qAbs(lines);     const int secIdxStart = start ? 0 : (-1 - (lines - 1));     .... } <\/code><\/pre>\n<p>Perhaps, this is not an error. However, we can greatly simplify the code. So, here&#8217;s what it looks like:<\/p>\n<pre><code>const int secIdxStart = start ? 0 : -lines ; <\/code><\/pre>\n<p>On the other hand, the negative value as a position looks strange. <\/p>\n<h3>Pointers in C++: a timeless classic<\/h3>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v522\/\">V522<\/a> Dereferencing of the null pointer &#8216;family&#8217; might take place. instrtemplate.cpp 356<\/p>\n<pre><code>void InstrumentTemplate::write(XmlWriter&amp; xml) const {   ....   if (!family) {     xml.tag(\"family\", family->id);   }   xml.etag(); } <\/code><\/pre>\n<p>Inasmuch as the extra negation was written in the conditional expression, the added &#171;family&#187; tag can spell disaster.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v522\/\">V522<\/a> Dereferencing of the null pointer &#8216;destinationMeasure&#8217; might take place. score.cpp 4279<\/p>\n<pre><code>ChordRest* Score::cmdNextPrevSystem(ChordRest* cr, bool next) {   ....   auto destinationMeasure = currentSystem->firstMeasure();   ....   if (!(destinationMeasure = destinationMeasure->prevMeasure())) {     if (!(destinationMeasure = destinationMeasure->prevMeasureMM())) {         return cr;     }   }   .... } <\/code><\/pre>\n<p>This is a similar but less obvious situation. Here access to the <em>destinationMeasure<\/em> pointer in a nested conditional expression takes place. It&#8217;s dereferencing the null pointer.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v595\/\">V595<\/a> The &#8216;fd&#8217; pointer was utilized before it was verified against nullptr. Check lines: 5365, 5366. edit.cpp 5365<\/p>\n<pre><code>void Score::undoAddElement(Element* element) {   ....   FretDiagram* fd = toFretDiagram(ne);   Harmony* fdHarmony = fd->harmony();   if (fd) {     fdHarmony->setScore(score);     fdHarmony->setSelected(false);     fdHarmony->setTrack(staffIdx * VOICES + element->voice());   }   .... } <\/code><\/pre>\n<p>Fret Diagram (or FretBoard) is also used to record melodies \u2013 for instance, by guitarists. However, they are a bit out of luck. The error here is that the <em>fd<\/em> pointer is dereferenced before its validity is checked. The name of the function suggests that it happens when the addition of an element gets canceled. That is, the rollback of some changes in the notes can accidentally break the program. Thus, you&#8217;ll probably lose the notes.<\/p>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v595\/\">V595<\/a> The &#8216;startSegment&#8217; pointer was utilized before it was verified against nullptr. Check lines: 129, 131. notationselectionrange.cpp 129<\/p>\n<pre><code>Ms::Segment* NotationSelectionRange::rangeStartSegment() const {   Ms::Segment* startSegment = score()->selection().startSegment();    startSegment->measure()->firstEnabled();  \/\/ &lt;=    if (!startSegment) {                      \/\/ &lt;=     return nullptr;   }    if (!startSegment->enabled()) {     startSegment = startSegment->next1MMenabled();   }   .... } <\/code><\/pre>\n<p>Unlike the previous code snippet, it seems to be failed refactoring. Most likely, the line dereferencing the <em>startSegment<\/em> pointer was added later. Moreover, it was displaced. It stands before the pointer validation.<\/p>\n<p>These were the most obvious warnings from this diagnostic. They were several lines apart from each other. Here&#8217;s a list of some other places that are worth viewing:<\/p>\n<ul>\n<li>\n<p>V595 The &#8216;note&#8217; pointer was utilized before it was verified against nullptr. Check lines: 5932, 5941. importmxmlpass2.cpp 5932<\/p>\n<\/li>\n<li>\n<p>V595 The &#8216;ed&#8217; pointer was utilized before it was verified against nullptr. Check lines: 599, 608. textedit.cpp 599<\/p>\n<\/li>\n<li>\n<p>V595 The &#8216;s&#8217; pointer was utilized before it was verified against nullptr. Check lines: 139, 143. elements.cpp 139<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v774\/\">V774<\/a> The &#8216;slur&#8217; pointer was used after the memory was released. importgtp-gp6.cpp 2592<\/p>\n<pre><code>void GuitarPro6::readGpif(QByteArray* data) {   ....   if (c) {     slur->setTick2(c->tick());     score->addElement(slur);     legatos[slur->track()] = 0;   } else {     delete slur;     legatos[slur->track()] = 0;   }   .... } <\/code><\/pre>\n<p>After the memory has been released, the data may still be in the same place for some time. So, no error will occur. However, you can&#8217;t rely on it. Besides, MuseScore is built for various platforms. This code may behave differently just after changing the compiler. In such a situation it&#8217;s better to swap the lines and correct a potential error. Also, it&#8217;s unclear why the memory is freed only in one branch of the code. <\/p>\n<h3>Miscellaneous warnings<\/h3>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p><a href=\"https:\/\/www.viva64.com\/en\/w\/v637\/\">V637<\/a> Two opposite conditions<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-400796","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400796","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=400796"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/400796\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=400796"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=400796"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=400796"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}