From b65b6bd5a6d571ad7047d85508f85c62ca9ad8ce Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 6 Jul 2016 11:14:17 +0200 Subject: Fix QQuickDefaultClipNode::updateGeometry() There are segments top + border and for each segment 2 points -> 4. Change-Id: I6df11e557054e4b942de430bd2cad8e2f798b0db Task-number: QTBUG-51894 Reviewed-by: Robin Burchell Reviewed-by: Laszlo Agocs --- src/quick/items/qquickclipnode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickclipnode.cpp b/src/quick/items/qquickclipnode.cpp index a46f758c3b..3b5933d910 100644 --- a/src/quick/items/qquickclipnode.cpp +++ b/src/quick/items/qquickclipnode.cpp @@ -87,7 +87,7 @@ void QQuickDefaultClipNode::updateGeometry() int segments = qMin(30, qCeil(radius)); // Number of segments per corner. - g->allocate((segments + 1) * 2); + g->allocate((segments + 1) * 4); QVector2D *vertices = (QVector2D *)g->vertexData(); -- cgit v1.2.3 From ebf07c3f68415099132856b2831633c310bc3395 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 14 Apr 2015 14:55:24 +0200 Subject: Flickable: avoid infinite velocity during release after drag It sometimes happens on touchscreens that mouse events occur too close together. We cannot calculate velocity based on zero elapsed time, so just ignore the event. Task-number: QTBUG-45527 Change-Id: I120e73cfa60e2fcc594cb1f3b69f530e746abddd Reviewed-by: Timur Pocheptsov --- src/quick/items/qquickflickable.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index b0980cd2c1..760eeed452 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1224,13 +1224,17 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) return; qint64 currentTimestamp = computeCurrentTime(event); - qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.; QVector2D deltas = QVector2D(event->localPos() - pressPos); bool overThreshold = false; QVector2D velocity = QGuiApplicationPrivate::mouseEventVelocity(event); // TODO guarantee that events always have velocity so that it never needs to be computed here - if (!(QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)) + if (!(QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)) { + qint64 lastTimestamp = (lastPos.isNull() ? lastPressTime : lastPosTime); + if (currentTimestamp == lastTimestamp) + return; // events are too close together: velocity would be infinite + qreal elapsed = qreal(currentTimestamp - lastTimestamp) / 1000.; velocity = QVector2D(event->localPos() - (lastPos.isNull() ? pressPos : lastPos)) / elapsed; + } if (q->yflick()) overThreshold |= QQuickWindowPrivate::dragOverThreshold(deltas.y(), Qt::YAxis, event); -- cgit v1.2.3 From 837c5d6284eeec8adb9b2caa3a0ce82b0da019a1 Mon Sep 17 00:00:00 2001 From: Filipe Azevedo Date: Fri, 5 Aug 2016 13:18:44 +0200 Subject: Fix crash when cache property can not be found For some reason if a cache property is not found this cause an application crash instead of just reporting an error. Now instead of a crash we get this kind of error visible: qrc:///the_file.qml line 64 column 30: Cannot assign object to property Change-Id: Ic420713df30603f1d164da439cba30a18af8f2bc Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmltypecompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 7e4ee344ff..82332dfc35 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1414,7 +1414,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI continue; QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType); - const QMetaObject *mo = pc->firstCppMetaObject(); + const QMetaObject *mo = pc ? pc->firstCppMetaObject() : 0; while (mo) { if (mo == &QQmlComponent::staticMetaObject) break; -- cgit v1.2.3 From ff645deb25c18eeae3d248a6a60bc2954129ee28 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Tue, 2 Aug 2016 11:52:58 +0200 Subject: Remove artifacts in font rendering The distancefield cache did not clear the textures before using them. Hence, random values could leak through in the edges of the distancefields, leading to random pixels at the edges of the rendered glyphs. This issue was rarely visible before, because of the way the glyphs were stacked on the textures. That stacking was changed as a result of 7190aa26f65ab97b4f54c156a107ed7748a11df5, which made the issue happen more often, so it was detected by lancelot. Change-Id: Ibe7a20dd7ba557ab92966e714c25a100e218ed24 Reviewed-by: Yoann Lopes --- src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index 728d8b6541..31275b304d 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -253,7 +253,8 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int const GLenum format = GL_ALPHA; #endif - m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, 0); + QByteArray zeroBuf(width * height, 0); + m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData()); texInfo->size = QSize(width, height); -- cgit v1.2.3 From 68bfc9332cd65c1eb88d1ec87164447b0db43237 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 25 Jul 2016 16:07:16 +0200 Subject: Fix crash with Component.onDestruction A call to a handler of Component.onDestruction may end up causing WeakValues such as QQmlData::jsWrapper to be set again, even though they've been set to undefined in an earlier iteration of the loop that walks through the weak references. That in turn may result in invalid object references to objects that are scheduled for destruction by the collector. So after calling all destroy handlers for QObjects, reset all of the weak values again. Task-number: QTBUG-54939 Change-Id: I00ebabb76274e296fb1bd90d8d3e21dbbb920b57 Reviewed-by: Lars Knoll --- src/qml/memory/qv4mm.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 7ab6d15041..4592dd5c9b 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -428,7 +428,7 @@ void MemoryManager::sweep(bool lastSweep) Managed *m = (*it).as(); if (m->markBit()) continue; - // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed + // we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed // signal before we start sweeping the heap if (QObjectWrapper *qobjectWrapper = (*it).as()) qobjectWrapper->destroyObject(lastSweep); @@ -436,6 +436,17 @@ void MemoryManager::sweep(bool lastSweep) (*it) = Primitive::undefinedValue(); } + // onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure + // that they are all set to undefined. + for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { + if (!(*it).isManaged()) + continue; + Managed *m = (*it).as(); + if (m->markBit()) + continue; + (*it) = Primitive::undefinedValue(); + } + // Now it is time to free QV4::QObjectWrapper Value, we must check the Value's tag to make sure its object has been destroyed const int pendingCount = m_pendingFreedObjectWrapperValue.count(); if (pendingCount) { -- cgit v1.2.3 From 13a427475d3638de843f33145378587037841a86 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 5 Aug 2016 12:33:03 +0200 Subject: Clarify doc on modification of the global object in JS imported by QML Currently, reading the documentation for modification of the global object in JavaScript can be confusing. http://doc.qt.io/qt-5/qtqml-javascript-hostenvironment.html says: JavaScript code cannot modify the global object. In QML, the global object is constant - existing properties cannot be modified or deleted, and no new properties may be created. ... Any attempt to modify the global object - either implicitly or explicitly - will cause an exception. If uncaught, this will result in a warning being printed, that includes the file and line number of the offending code. http://doc.qt.io/qt-5/qjsengine.html#globalObject says: Returns this engine's Global Object. By default, the Global Object contains the built-in objects that are part of ECMA-262, such as Math, Date and String. Additionally, you can set properties of the Global Object to make your own extensions available to all script code. Non-local variables in script code will be created as properties of the Global Object, as well as local variables in global code. If QQmlEngine "is-a" QJSEngine, and QJSEngine can have its global object modified, it might seem reasonable to expect that imported JavaScript code should be able to modify the global object. This patch aims to be more explicit about the restrictions and give examples of how libraries should expose their APIs correctly for use by QML code. Change-Id: I11beb894a88d52038be90ffe6baa9337943810db Reviewed-by: Simon Hausmann --- src/qml/doc/src/javascript/hostenvironment.qdoc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index de8b967d72..1e33f2f641 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -80,7 +80,10 @@ Note that QML makes the following modifications to native objects: QML implements the following restrictions for JavaScript code: \list -\li JavaScript code cannot modify the global object. +\li JavaScript code written in a \c .qml file cannot modify the global object. + JavaScript code in a .js file can modify the global object, + and those modifications will be visible to the .qml file when + \l {Importing a JavaScript Resource from a QML Document}{imported}. In QML, the global object is constant - existing properties cannot be modified or deleted, and no new properties may be created. -- cgit v1.2.3 From 31ca52cee47f189ee1c80245f7b114c29ac7c675 Mon Sep 17 00:00:00 2001 From: Eike Hein Date: Thu, 4 Jun 2015 21:25:32 +0200 Subject: Don't accept left clicks when text format is plain. Text elements may contain rich text with embedded links, and need to accept left clicks to open them. However, setting the textFormat to PlainText can disable mouse handling entirely, as it is not required. Accepting left clicks if there can be nothing to interact with is unexpected and surprising, and can cause bugs in code that performs child event filtering and doesn't expect Text elements to produce child events. Change-Id: Ibd5b9cf8d06fd30ea26f78b5393cc43e94646e73 Reviewed-by: Marco Martin Reviewed-by: Kai Uwe Broulik Reviewed-by: Robin Burchell Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktext.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index e67e2cee9c..ea2d2f3133 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2013,6 +2013,7 @@ void QQuickText::setTextFormat(TextFormat format) } d->updateLayout(); setAcceptHoverEvents(d->richText || d->styledText); + setAcceptedMouseButtons(d->richText || d->styledText ? Qt::LeftButton : Qt::NoButton); emit textFormatChanged(d->format); } -- cgit v1.2.3 From 35597f301480ffc59f598be4de2c2074543725be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Thu, 2 Jun 2016 12:08:29 +0200 Subject: Fix char conversions in QML This is a partial revert of 90b06e2773842, as it had unwanted side effects. The original intention was to make assignment from char to string possible, or more specifically, we wanted a solution where a QChar could be assigned to a QString, as a character and not a string representation of its value. While this behavior is desirable for QChar, we most likely want the opposite for the regular character types. Task-number: QTBUG-49232 Change-Id: I82d5f72b900fe984c4db1478fd52a9eb69ad2ee6 Reviewed-by: Michael Brasser Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 13 +++++++++---- src/qml/qml/qqmlproperty.cpp | 25 ++----------------------- 2 files changed, 11 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5dc3e6151f..557b678ef8 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1143,8 +1143,13 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return value.integerValue(); if (value.isNumber()) return value.asDouble(); - if (value.isString()) - return value.stringValue()->toQString(); + if (value.isString()) { + const QString &str = value.toQString(); + // QChars are stored as a strings + if (typeHint == QVariant::Char && str.size() == 1) + return str.at(0); + return str; + } if (const QV4::QQmlLocaleData *ld = value.as()) return ld->d()->locale; if (const QV4::DateObject *d = value.as()) @@ -1284,9 +1289,9 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) case QMetaType::UShort: return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::Char: - return newString(QChar::fromLatin1(*reinterpret_cast(ptr)))->asReturnedValue(); + return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::UChar: - return newString(QChar::fromLatin1(*reinterpret_cast(ptr)))->asReturnedValue(); + return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::QChar: return newString(*reinterpret_cast(ptr))->asReturnedValue(); case QMetaType::QDateTime: diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 1b78ada698..1eaff6b600 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1327,29 +1327,8 @@ bool QQmlPropertyPrivate::write(QObject *object, bool ok = false; QVariant v; - if (variantType == QVariant::String) { - const QString &str = value.toString(); - const bool targetIsChar = (propertyType == qMetaTypeId() - || propertyType == qMetaTypeId() - || propertyType == qMetaTypeId()); - // If the string contains only one character and the target is a char, try converting it. - if (targetIsChar) { - if (str.size() != 1) - return false; // We can only convert if the string contains exactly one character. - - const QChar &qChar = str.at(0); - if (propertyType == qMetaTypeId()) { - v = qChar; - ok = true; - } else if (propertyType == qMetaTypeId() || propertyType == qMetaTypeId()) { - const char c = qChar.toLatin1(); - v = c; - ok = (qChar == c); - } - } else { - v = QQmlStringConverters::variantFromString(str, propertyType, &ok); - } - } + if (variantType == QVariant::String) + v = QQmlStringConverters::variantFromString(value.toString(), propertyType, &ok); if (!ok) { v = value; -- cgit v1.2.3 From 4493524ec24afb946eba3942f48d9fc1ff3192c1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 9 Aug 2016 10:49:22 +0200 Subject: V4: Align stack on 16 byte boundaries in the YarrJIT This is the required alignment for Aarch64, and a number of other ABIs prefer this size too when calling into system libraries. Change-Id: Ie38cabb77cf83543b915553e69c5c5728a67503b Reviewed-by: Simon Hausmann --- src/3rdparty/masm/yarr/YarrJIT.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp index 5664c585b9..d8211ec4b2 100644 --- a/src/3rdparty/masm/yarr/YarrJIT.cpp +++ b/src/3rdparty/masm/yarr/YarrJIT.cpp @@ -338,17 +338,31 @@ class YarrGenerator : private MacroAssembler { jump(Address(stackPointerRegister, frameLocation * sizeof(void*))); } + unsigned alignCallFrameSizeInBytes(unsigned callFrameSize) + { + callFrameSize *= sizeof(void*); + if (callFrameSize / sizeof(void*) != m_pattern.m_body->m_callFrameSize) + CRASH(); + // Originally, the code was: +// callFrameSize = (callFrameSize + 0x3f) & ~0x3f; + // However, 64 bytes is a bit surprising. The biggest "alignment" requirement is on Aarch64, where: + // "SP mod 16 = 0. The stack must be quad-word aligned." (IHI0055B_aapcs64.pdf) + callFrameSize = (callFrameSize + 0xf) & ~0xf; + if (!callFrameSize) + CRASH(); + return callFrameSize; + } void initCallFrame() { unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; if (callFrameSize) - subPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + subPtr(Imm32(alignCallFrameSizeInBytes(callFrameSize)), stackPointerRegister); } void removeCallFrame() { unsigned callFrameSize = m_pattern.m_body->m_callFrameSize; if (callFrameSize) - addPtr(Imm32(callFrameSize * sizeof(void*)), stackPointerRegister); + addPtr(Imm32(alignCallFrameSizeInBytes(callFrameSize)), stackPointerRegister); } // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns. @@ -2565,6 +2579,10 @@ class YarrGenerator : private MacroAssembler { if (compileMode == IncludeSubpatterns) loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output); #endif +#elif CPU(ARM64) + // The ABI doesn't guarantee the upper bits are zero on unsigned arguments, so clear them ourselves. + zeroExtend32ToPtr(index, index); + zeroExtend32ToPtr(length, length); #elif CPU(ARM) push(ARMRegisters::r4); push(ARMRegisters::r5); -- cgit v1.2.3 From 580f2872f09cf7ad83ec9ae5dca686683a3cac80 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 9 Aug 2016 15:14:55 +0200 Subject: TextInput: fix horizontal alignment when implicit resizing is disabled By default, TextInput updates its implicit width to match the content width. This implies that when TextInput does not have an explicit width set, there is no need to handle horizontal alignment. TextField wants to override the default implicit width, so it disables the "implicit resizing" feature of TextInput. In this scenario, the implicit width does not match the content width. Therefore the text layouting code should also treat the item as if it had an explicit width set to achieve the correct horizontal alignment. Task-number: QTBUG-55138 Change-Id: I8c04971a6aff44c6f1734df50153a9788849e98a Reviewed-by: Mitch Curtis --- src/quick/items/qquicktextinput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 03c6b892c4..63e8505ffa 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -2916,7 +2916,7 @@ void QQuickTextInputPrivate::updateLayout() if (inLayout) // probably the result of a binding loop, but by letting it return; // get this far we'll get a warning to that effect. } - qreal lineWidth = q->widthValid() ? q->width() - q->leftPadding() - q->rightPadding() : INT_MAX; + qreal lineWidth = q->widthValid() || !isImplicitResizeEnabled() ? q->width() - q->leftPadding() - q->rightPadding() : INT_MAX; qreal height = 0; qreal width = 0; do { -- cgit v1.2.3 From fdd8bf7dbf91b8e8ce84c990a03767c0b8ba064a Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 8 Jun 2016 10:02:29 +0200 Subject: QQmlObjectCreator: initialize _bindingTarget Coverity (CID 163180) noticed _bindingTarget wasn't initialized. Change-Id: Ia727d00a161e514c437a72084b6ef01a7ebf4abc Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 0c13a7a017..c06e1827ac 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -114,6 +114,7 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) context = 0; _qobject = 0; _scopeObject = 0; + _bindingTarget = 0; _valueTypeProperty = 0; _compiledObject = 0; _compiledObjectIndex = -1; -- cgit v1.2.3 From b054a700576f61d9fbd196987f42fdea7a806db1 Mon Sep 17 00:00:00 2001 From: Edward Welbourne Date: Wed, 8 Jun 2016 10:03:37 +0200 Subject: QQmlObjectCreator: prefer initializer syntax over assignment Greater uniformity; also opens the door to potential const-ing, should this ever be worht considering. Change-Id: I91b44472cb7d84f85b3033f14a763beeea837459 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index c06e1827ac..8fa1f48d18 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -68,12 +68,12 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile , resolvedTypes(compiledData->resolvedTypes) , propertyCaches(compiledData->propertyCaches) , vmeMetaObjectData(compiledData->metaObjects) + , sharedState(new QQmlObjectCreatorSharedState) + , topLevelCreator(true) , activeVMEDataForRootContext(activeVMEDataForRootContext) { init(parentContext); - sharedState = new QQmlObjectCreatorSharedState; - topLevelCreator = true; sharedState->componentAttached = 0; sharedState->allCreatedBindings.allocate(compiledData->totalBindingsCount); sharedState->allParserStatusCallbacks.allocate(compiledData->totalParserStatusCount); @@ -93,12 +93,11 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QQmlCompile , resolvedTypes(compiledData->resolvedTypes) , propertyCaches(compiledData->propertyCaches) , vmeMetaObjectData(compiledData->metaObjects) + , sharedState(inheritedSharedState) + , topLevelCreator(false) , activeVMEDataForRootContext(0) { init(parentContext); - - sharedState = inheritedSharedState; - topLevelCreator = false; } void QQmlObjectCreator::init(QQmlContextData *providedParentContext) -- cgit v1.2.3 From b4327851a4895b28b2c60ca51e77eca3ee0a1b4e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 12 Aug 2016 08:02:37 +0200 Subject: Move the moth stack slot allocator into qmldevtools This way it's accessible to the QML compiler. Change-Id: I3918a796c698fc75e134b29a61eed2ec028bc851 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4isel_moth.cpp | 163 +------------------------------------- src/qml/compiler/qv4ssa_p.h | 163 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 162 deletions(-) (limited to 'src') diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index afb36c5f14..f87cbe3f1b 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -143,167 +143,6 @@ inline bool isBoolType(IR::Expr *e) return (e->type == IR::BoolType); } -/* - * stack slot allocation: - * - * foreach bb do - * foreach stmt do - * if the current statement is not a phi-node: - * purge ranges that end before the current statement - * check for life ranges to activate, and if they don't have a stackslot associated then allocate one - * renumber temps to stack - * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated - * if it's a jump: - * foreach phi node in the successor: - * allocate slots for each temp (both sources and targets) if they don't have one allocated already - * insert moves before the jump - */ -class AllocateStackSlots: protected ConvertTemps -{ - IR::LifeTimeIntervals::Ptr _intervals; - QVector _unhandled; - QVector _live; - QBitArray _slotIsInUse; - IR::Function *_function; - - int defPosition(IR::Stmt *s) const - { - return usePosition(s) + 1; - } - - int usePosition(IR::Stmt *s) const - { - return _intervals->positionForStatement(s); - } - -public: - AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals) - : _intervals(intervals) - , _slotIsInUse(intervals->size(), false) - , _function(0) - { - _live.reserve(8); - _unhandled = _intervals->intervals(); - } - - void forFunction(IR::Function *function) - { - IR::Optimizer::showMeTheCode(function, "Before stack slot allocation"); - _function = function; - toStackSlots(function); - } - -protected: - virtual int allocateFreeSlot() - { - for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { - if (!_slotIsInUse[i]) { - if (_nextUnusedStackSlot <= i) { - Q_ASSERT(_nextUnusedStackSlot == i); - _nextUnusedStackSlot = i + 1; - } - _slotIsInUse[i] = true; - return i; - } - } - - Q_UNREACHABLE(); - return -1; - } - - virtual void process(IR::Stmt *s) - { -// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); - - if (IR::Phi *phi = s->asPhi()) { - visitPhi(phi); - } else { - // purge ranges no longer alive: - for (int i = 0; i < _live.size(); ) { - const IR::LifeTimeInterval *lti = _live.at(i); - if (lti->end() < usePosition(s)) { -// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; - _live.remove(i); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); - _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; - continue; - } else { - ++i; - } - } - - // active new ranges: - while (!_unhandled.isEmpty()) { - IR::LifeTimeInterval *lti = _unhandled.last(); - if (lti->start() > defPosition(s)) - break; // we're done - Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); - _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); -// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; - _live.append(lti); - _unhandled.removeLast(); - } - - s->accept(this); - } - - if (IR::Jump *jump = s->asJump()) { - IR::MoveMapping moves; - foreach (IR::Stmt *succStmt, jump->target->statements()) { - if (IR::Phi *phi = succStmt->asPhi()) { - forceActivation(*phi->targetTemp); - for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i) { - IR::Expr *e = phi->d->incoming[i]; - if (IR::Temp *t = e->asTemp()) { - forceActivation(*t); - } - if (jump->target->in[i] == _currentBasicBlock) - moves.add(phi->d->incoming[i], phi->targetTemp); - } - } else { - break; - } - } - moves.order(); - QList newMoves = moves.insertMoves(_currentBasicBlock, _function, true); - foreach (IR::Move *move, newMoves) - move->accept(this); - } - } - - void forceActivation(const IR::Temp &t) - { - if (_stackSlotForTemp.contains(t.index)) - return; - - int i = _unhandled.size() - 1; - for (; i >= 0; --i) { - IR::LifeTimeInterval *lti = _unhandled[i]; - if (lti->temp() == t) { - _live.append(lti); - _unhandled.remove(i); - break; - } - } - Q_ASSERT(i >= 0); // check that we always found the entry - - _stackSlotForTemp[t.index] = allocateFreeSlot(); -// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; - } - - virtual void visitPhi(IR::Phi *phi) - { - Q_UNUSED(phi); -#if !defined(QT_NO_DEBUG) - Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); - Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); - foreach (IR::Expr *e, phi->d->incoming) { - if (IR::Temp *t = e->asTemp()) - Q_ASSERT(_stackSlotForTemp.contains(t->index)); - } -#endif // defined(QT_NO_DEBUG) - } -}; } // anonymous namespace InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) @@ -353,7 +192,7 @@ void InstructionSelection::run(int functionIndex) qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION"); if (doStackSlotAllocation) { - AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); + IR::AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function); } else { opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index d06774e803..c5690a8581 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -46,6 +46,7 @@ // #include "qv4jsir_p.h" +#include "qv4isel_util_p.h" #include QT_BEGIN_NAMESPACE @@ -271,6 +272,168 @@ private: QList &swaps) const; }; +/* + * stack slot allocation: + * + * foreach bb do + * foreach stmt do + * if the current statement is not a phi-node: + * purge ranges that end before the current statement + * check for life ranges to activate, and if they don't have a stackslot associated then allocate one + * renumber temps to stack + * for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated + * if it's a jump: + * foreach phi node in the successor: + * allocate slots for each temp (both sources and targets) if they don't have one allocated already + * insert moves before the jump + */ +class AllocateStackSlots: protected ConvertTemps +{ + IR::LifeTimeIntervals::Ptr _intervals; + QVector _unhandled; + QVector _live; + QBitArray _slotIsInUse; + IR::Function *_function; + + int defPosition(IR::Stmt *s) const + { + return usePosition(s) + 1; + } + + int usePosition(IR::Stmt *s) const + { + return _intervals->positionForStatement(s); + } + +public: + AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals) + : _intervals(intervals) + , _slotIsInUse(intervals->size(), false) + , _function(0) + { + _live.reserve(8); + _unhandled = _intervals->intervals(); + } + + void forFunction(IR::Function *function) + { + IR::Optimizer::showMeTheCode(function, "Before stack slot allocation"); + _function = function; + toStackSlots(function); + } + +protected: + virtual int allocateFreeSlot() + { + for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) { + if (!_slotIsInUse[i]) { + if (_nextUnusedStackSlot <= i) { + Q_ASSERT(_nextUnusedStackSlot == i); + _nextUnusedStackSlot = i + 1; + } + _slotIsInUse[i] = true; + return i; + } + } + + Q_UNREACHABLE(); + return -1; + } + + virtual void process(IR::Stmt *s) + { +// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id); + + if (IR::Phi *phi = s->asPhi()) { + visitPhi(phi); + } else { + // purge ranges no longer alive: + for (int i = 0; i < _live.size(); ) { + const IR::LifeTimeInterval *lti = _live.at(i); + if (lti->end() < usePosition(s)) { +// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index]; + _live.remove(i); + Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]); + _slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false; + continue; + } else { + ++i; + } + } + + // active new ranges: + while (!_unhandled.isEmpty()) { + IR::LifeTimeInterval *lti = _unhandled.last(); + if (lti->start() > defPosition(s)) + break; // we're done + Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index)); + _stackSlotForTemp[lti->temp().index] = allocateFreeSlot(); +// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index]; + _live.append(lti); + _unhandled.removeLast(); + } + + s->accept(this); + } + + if (IR::Jump *jump = s->asJump()) { + IR::MoveMapping moves; + foreach (IR::Stmt *succStmt, jump->target->statements()) { + if (IR::Phi *phi = succStmt->asPhi()) { + forceActivation(*phi->targetTemp); + for (int i = 0, ei = phi->d->incoming.size(); i != ei; ++i) { + IR::Expr *e = phi->d->incoming[i]; + if (IR::Temp *t = e->asTemp()) { + forceActivation(*t); + } + if (jump->target->in[i] == _currentBasicBlock) + moves.add(phi->d->incoming[i], phi->targetTemp); + } + } else { + break; + } + } + moves.order(); + QList newMoves = moves.insertMoves(_currentBasicBlock, _function, true); + foreach (IR::Move *move, newMoves) + move->accept(this); + } + } + + void forceActivation(const IR::Temp &t) + { + if (_stackSlotForTemp.contains(t.index)) + return; + + int i = _unhandled.size() - 1; + for (; i >= 0; --i) { + IR::LifeTimeInterval *lti = _unhandled[i]; + if (lti->temp() == t) { + _live.append(lti); + _unhandled.remove(i); + break; + } + } + Q_ASSERT(i >= 0); // check that we always found the entry + + _stackSlotForTemp[t.index] = allocateFreeSlot(); +// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index]; + } + + virtual void visitPhi(IR::Phi *phi) + { + Q_UNUSED(phi); +#if !defined(QT_NO_DEBUG) + Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index)); + Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]); + foreach (IR::Expr *e, phi->d->incoming) { + if (IR::Temp *t = e->asTemp()) + Q_ASSERT(_stackSlotForTemp.contains(t->index)); + } +#endif // defined(QT_NO_DEBUG) + } +}; + } // IR namespace } // QV4 namespace -- cgit v1.2.3 From 273e5b0bdc1239df77e2e5694fdd5ad068595be3 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 24 May 2016 12:21:39 +0300 Subject: PathView: fix item creation First call of QQuickPathView::refill() did not use currentIndex for item prepending and there was situation when items were not created, e.g.: PathView with current item in center and currentIndex was set so that item with index 0 was after current item and before path end. The result of this situation: items from path begin to current item were not created. The reason was that idx always equaled (modelCount-1) for item prepending. Now first filling uses currentIndex to calculate valid idx. Task-number: QTBUG-53464 Change-Id: I7e343b0712c9c5c5cd56b1d8e020cf8c0f6e6301 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 7e1fa95692..1dbdd10303 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -1854,6 +1854,14 @@ void QQuickPathView::updatePolish() refill(); } +static inline int currentIndexRemainder(int currentIndex, int modelCount) Q_DECL_NOTHROW +{ + if (currentIndex < 0) + return modelCount + currentIndex % modelCount; + else + return currentIndex % modelCount; +} + void QQuickPathView::componentComplete() { Q_D(QQuickPathView); @@ -1865,7 +1873,7 @@ void QQuickPathView::componentComplete() if (d->model) { d->modelCount = d->model->count(); if (d->modelCount && d->currentIndex != 0) // an initial value has been provided for currentIndex - d->offset = qmlMod(d->modelCount - d->currentIndex, d->modelCount); + d->offset = qmlMod(d->modelCount - currentIndexRemainder(d->currentIndex, d->modelCount), d->modelCount); } d->createHighlight(); @@ -1939,7 +1947,8 @@ void QQuickPathView::refill() qreal endPos; int startIdx = 0; qreal startPos = 0.0; - if (d->items.count()) { + const bool wasEmpty = d->items.isEmpty(); + if (!wasEmpty) { //Find the beginning and end, items may not be in sorted order endPos = -1.0; startPos = 2.0; @@ -1998,7 +2007,8 @@ void QQuickPathView::refill() } //Prepend - idx = startIdx - 1; + idx = (wasEmpty ? d->calcCurrentIndex() : startIdx) - 1; + if (idx < 0) idx = d->modelCount - 1; nextPos = d->positionOfIndex(idx); -- cgit v1.2.3 From 6f15de1d2da9c83d7fca1d5c8243c0d69a1390ee Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Thu, 26 May 2016 14:45:31 +0300 Subject: PathView: fix infinite construction/destruction loop ... when all (or almost all) items are in the cache. When all items are in cache, check lower bound is equal to upper_bound. In rare cases, especially when almost all items are in cache, the inserting code was used (not only appending and prepending). In this code there was not bound check before creation of item and there was such situation: 1. Create item by inserting code (without bound check) 2. At the next call of refill() remove this item by life cycle because this item does not meet the conditions. And go to step 1. In other words at the first call we create some item, at the second remove this item. And again. So we had infinite construction/destruction loop. To break it we should check position of new item before creation in inserting code too (like we do in appending and prepending code). Task-number: QTBUG-37815 Change-Id: I015cdeb67ca5fcd06c34b3145b49cbd3e38d4078 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickpathview.cpp | 44 ++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 1dbdd10303..f858b18c84 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -289,6 +289,8 @@ qreal QQuickPathViewPrivate::positionOfIndex(qreal index) const // account the circular space. bool QQuickPathViewPrivate::isInBound(qreal position, qreal lower, qreal upper) const { + if (lower == upper) + return true; if (lower > upper) { if (position > upper && position > lower) position -= mappedRange; @@ -2047,27 +2049,33 @@ void QQuickPathView::refill() idx = startIdx; QQuickItem *lastItem = d->items[0]; while (idx != endIdx) { - //This gets the reference from the delegate model, and will not re-create - QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); - if (!item) { - waiting = true; - break; - } - if (!d->items.contains(item)) { //We found a hole - nextPos = d->positionOfIndex(idx); - qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos << (d->currentIndex == idx ? "current" : "") << "items count was" << d->items.count(); - if (d->currentIndex == idx) { - currentVisible = true; - d->currentItemOffset = nextPos; + nextPos = d->positionOfIndex(idx); + if (d->isInBound(nextPos, d->mappedRange - d->mappedCache, 1.0 + d->mappedCache)) { + //This gets the reference from the delegate model, and will not re-create + QQuickItem *item = d->getItem(idx, idx+1, nextPos >= 1.0); + if (!item) { + waiting = true; + break; } - int lastListIdx = d->items.indexOf(lastItem); - d->items.insert(lastListIdx + 1, item); - d->updateItem(item, nextPos); - } else { - d->releaseItem(item); + + if (!d->items.contains(item)) { //We found a hole + qCDebug(lcItemViewDelegateLifecycle) << "middle insert" << idx << "@" << nextPos + << (d->currentIndex == idx ? "current" : "") + << "items count was" << d->items.count(); + if (d->currentIndex == idx) { + currentVisible = true; + d->currentItemOffset = nextPos; + } + int lastListIdx = d->items.indexOf(lastItem); + d->items.insert(lastListIdx + 1, item); + d->updateItem(item, nextPos); + } else { + d->releaseItem(item); + } + + lastItem = item; } - lastItem = item; ++idx; if (idx >= d->modelCount) idx = 0; -- cgit v1.2.3 From c0f3c8ae5cee3ecda1ac8829336aa95cbe4d330e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Sun, 14 Aug 2016 12:20:45 +0200 Subject: Doc: mention when Flickable.AutoFlickIfNeeded was added Change-Id: Icf72c05c9573715771353bd03735f64eadd808f2 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 03b2a8128c..870a0268e1 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -886,7 +886,7 @@ QQuickFlickableVisibleArea *QQuickFlickable::visibleArea() \li Flickable.AutoFlickIfNeeded - allows flicking vertically if the \e contentHeight is greater than the \e height of the Flickable. Allows flicking horizontally if the \e contentWidth is greater than - to the \e width of the Flickable. + to the \e width of the Flickable. (since \c{QtQuick 2.7}) \li Flickable.HorizontalFlick - allows flicking horizontally. \li Flickable.VerticalFlick - allows flicking vertically. \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions. -- cgit v1.2.3