summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@qt.io>2016-09-30 02:59:43 +0000
committerThe Qt Project <gerrit-noreply@qt-project.org>2016-09-30 03:38:12 +0000
commite4134cfd8086a78c61a46fdfb908ade27c8d9840 (patch)
tree9514153cd35f7335238b08586671445283abbef6
parent0ca693407473eb9457cdf8f9d4be32c9d4968ced (diff)
parent0bf14044178d7aa212ac7e28530f9077790a3df4 (diff)
Merge "Merge remote-tracking branch 'origin/5.6' into 5.7" into refs/staging/5.7
-rw-r--r--examples/quick/quickwidgets/quickwidget/main.cpp30
-rw-r--r--examples/quick/quickwidgets/quickwidget/rotatingsquare.qml6
-rw-r--r--src/qml/debugger/qqmldebug.cpp18
-rw-r--r--src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc8
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc3
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/signals.qdoc3
-rw-r--r--src/qml/jit/qv4assembler.cpp38
-rw-r--r--src/qml/jit/qv4assembler_p.h5
-rw-r--r--src/qml/jit/qv4isel_masm.cpp98
-rw-r--r--src/qml/jit/qv4isel_masm_p.h4
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp13
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h2
-rw-r--r--src/qml/jsruntime/qv4value_p.h246
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp26
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp32
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h1
-rw-r--r--src/quick/items/qquickflickable.cpp4
-rw-r--r--src/quick/items/qquickmousearea.cpp2
-rw-r--r--src/quick/items/qquicktext.cpp6
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp6
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp9
-rw-r--r--tests/auto/quick/qquickflickable/data/wheel.qml5
-rw-r--r--tests/auto/quick/qquickflickable/tst_qquickflickable.cpp20
-rw-r--r--tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml44
-rw-r--r--tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp93
-rw-r--r--tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml25
-rw-r--r--tests/auto/quick/qquicktext/tst_qquicktext.cpp60
27 files changed, 617 insertions, 190 deletions
diff --git a/examples/quick/quickwidgets/quickwidget/main.cpp b/examples/quick/quickwidgets/quickwidget/main.cpp
index 65258d958e..3f3638a43f 100644
--- a/examples/quick/quickwidgets/quickwidget/main.cpp
+++ b/examples/quick/quickwidgets/quickwidget/main.cpp
@@ -39,6 +39,7 @@
****************************************************************************/
#include <QQuickWidget>
+#include <QQuickItem>
#include <QQmlError>
#include <QtWidgets>
@@ -50,8 +51,9 @@ public:
private slots:
void quickWidgetStatusChanged(QQuickWidget::Status);
void sceneGraphError(QQuickWindow::SceneGraphError error, const QString &message);
- void grabToFile();
- void renderToFile();
+ void grabFramebuffer();
+ void renderToPixmap();
+ void grabToImage();
private:
QQuickWidget *m_quickWidget;
@@ -91,8 +93,9 @@ MainWindow::MainWindow()
setCentralWidget(centralWidget);
QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
- fileMenu->addAction(tr("Grab to imFage"), this, &MainWindow::grabToFile);
- fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToFile);
+ fileMenu->addAction(tr("Grab framebuffer"), this, &MainWindow::grabFramebuffer);
+ fileMenu->addAction(tr("Render to pixmap"), this, &MainWindow::renderToPixmap);
+ fileMenu->addAction(tr("Grab via grabToImage"), this, &MainWindow::grabToImage);
fileMenu->addAction(tr("Quit"), qApp, &QCoreApplication::quit);
}
@@ -113,8 +116,7 @@ void MainWindow::sceneGraphError(QQuickWindow::SceneGraphError, const QString &m
template<class T> void saveToFile(QWidget *parent, T *saveable)
{
- QString t;
- QFileDialog fd(parent, t, QString());
+ QFileDialog fd(parent);
fd.setAcceptMode(QFileDialog::AcceptSave);
fd.setDefaultSuffix("png");
fd.selectFile("test.png");
@@ -122,19 +124,31 @@ template<class T> void saveToFile(QWidget *parent, T *saveable)
saveable->save(fd.selectedFiles().first());
}
-void MainWindow::grabToFile()
+void MainWindow::grabFramebuffer()
{
QImage image = m_quickWidget->grabFramebuffer();
saveToFile(this, &image);
}
-void MainWindow::renderToFile()
+void MainWindow::renderToPixmap()
{
QPixmap pixmap(m_quickWidget->size());
m_quickWidget->render(&pixmap);
saveToFile(this, &pixmap);
}
+void MainWindow::grabToImage()
+{
+ QFileDialog fd(this);
+ fd.setAcceptMode(QFileDialog::AcceptSave);
+ fd.setDefaultSuffix("png");
+ fd.selectFile("test_grabToImage.png");
+ if (fd.exec() == QDialog::Accepted) {
+ QMetaObject::invokeMethod(m_quickWidget->rootObject(), "performLayerBasedGrab",
+ Q_ARG(QVariant, fd.selectedFiles().first()));
+ }
+}
+
int main(int argc, char **argv)
{
QApplication app(argc, argv);
diff --git a/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml b/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml
index 0c25eddf88..03b1114d20 100644
--- a/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml
+++ b/examples/quick/quickwidgets/quickwidget/rotatingsquare.qml
@@ -57,4 +57,10 @@ Rectangle {
anchors.centerIn: parent
text: "Qt Quick running in a widget"
}
+
+ function performLayerBasedGrab(fn) {
+ root.grabToImage(function(result) {
+ result.saveToFile(fn);
+ });
+ }
}
diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp
index 9276bd0544..557cce08d5 100644
--- a/src/qml/debugger/qqmldebug.cpp
+++ b/src/qml/debugger/qqmldebug.cpp
@@ -184,4 +184,22 @@ bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName,
return false;
}
+enum { HookCount = 3 };
+
+// Only add to the end, and bump version if you do.
+quintptr Q_QML_EXPORT qtDeclarativeHookData[] = {
+ // Version of this Array. Bump if you add to end.
+ 1,
+
+ // Number of entries in this array.
+ HookCount,
+
+ // TypeInformationVersion, an integral value, bumped whenever private
+ // object sizes or member offsets that are used in Qt Creator's
+ // data structure "pretty printing" change.
+ 2
+};
+
+Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0]));
+
QT_END_NAMESPACE
diff --git a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
index 4b4b25796c..03a7ddfb19 100644
--- a/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/documents/definetypes.qdoc
@@ -43,7 +43,8 @@ For example, below is a document that declares a \l Rectangle with a child \l Mo
import QtQuick 2.0
Rectangle {
- width: 100; height: 100
+ property int side: 100
+ width: side; height: side
color: "red"
MouseArea {
@@ -87,7 +88,7 @@ For example, the root object type in the \c SquareButton.qml file above is \l Re
import QtQuick 2.0
Column {
- SquareButton { width: 50; height: 50 }
+ SquareButton { side: 50 }
SquareButton { x: 50; color: "blue" }
SquareButton { radius: 10 }
}
@@ -112,7 +113,8 @@ Rectangle {
root.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
}
- width: 100; height: 100
+ property int side: 100
+ width: side; height: side
color: "red"
MouseArea {
diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
index 3692605c18..db9eee7714 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc
@@ -724,7 +724,8 @@ Rectangle {
signal activated(real xPosition, real yPosition)
signal deactivated
- width: 100; height: 100
+ property int side: 100
+ width: side; height: side
MouseArea {
anchors.fill: parent
diff --git a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
index 602b202ed4..4a022f2b0b 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/signals.qdoc
@@ -187,7 +187,8 @@ Rectangle {
signal activated(real xPosition, real yPosition)
- width: 100; height: 100
+ property int side: 100
+ width: side; height: side
MouseArea {
anchors.fill: parent
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index 3da1aaa010..4d5d090088 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -139,8 +139,30 @@ void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBl
generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock);
}
-void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left,TrustedImm32 right,
- IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
+#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+void Assembler::generateCJumpOnCompare(RelationalCondition cond,
+ RegisterID left,
+ TrustedImm64 right,
+ IR::BasicBlock *currentBlock,
+ IR::BasicBlock *trueBlock,
+ IR::BasicBlock *falseBlock)
+{
+ if (trueBlock == _nextBlock) {
+ Jump target = branch64(invert(cond), left, right);
+ addPatch(falseBlock, target);
+ } else {
+ Jump target = branch64(cond, left, right);
+ addPatch(trueBlock, target);
+ jumpToBlock(currentBlock, falseBlock);
+ }
+}
+#endif
+
+void Assembler::generateCJumpOnCompare(RelationalCondition cond,
+ RegisterID left,
+ TrustedImm32 right,
+ IR::BasicBlock *currentBlock,
+ IR::BasicBlock *trueBlock,
IR::BasicBlock *falseBlock)
{
if (trueBlock == _nextBlock) {
@@ -153,8 +175,11 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left
}
}
-void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right,
- IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
+void Assembler::generateCJumpOnCompare(RelationalCondition cond,
+ RegisterID left,
+ RegisterID right,
+ IR::BasicBlock *currentBlock,
+ IR::BasicBlock *trueBlock,
IR::BasicBlock *falseBlock)
{
if (trueBlock == _nextBlock) {
@@ -338,9 +363,8 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe
// not an int, check if it's a double:
isNoInt.link(this);
#ifdef QV4_USE_64_BIT_VALUE_ENCODING
- and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister);
- Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister,
- Assembler::TrustedImm32(0));
+ rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister);
+ Assembler::Jump isNoDbl = branch32(Equal, ScratchRegister, TrustedImm32(0));
#else
and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister);
Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister,
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h
index a5028bc683..234254513d 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4assembler_p.h
@@ -380,6 +380,11 @@ public:
void addPatch(DataLabelPtr patch, IR::BasicBlock *target);
void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock,
IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
+#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+ void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm64 right,
+ IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
+ IR::BasicBlock *falseBlock);
+#endif
void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right,
IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock,
IR::BasicBlock *falseBlock);
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index bf658fe689..1bb62f40b6 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -709,7 +709,7 @@ void InstructionSelection::loadString(const QString &str, IR::Expr *target)
#else
_as->store32(Assembler::ReturnValueRegister, destAddr);
destAddr.offset += 4;
- _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type), destAddr);
+ _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type_Internal), destAddr);
#endif
}
@@ -1109,7 +1109,7 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe
// not an int, check if it's NOT a double:
isNoInt.link(_as);
#ifdef QV4_USE_64_BIT_VALUE_ENCODING
- _as->and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister);
+ _as->rshift32(Assembler::TrustedImm32(Value::IsDoubleTag_Shift), Assembler::ScratchRegister);
Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister,
Assembler::TrustedImm32(0));
#else
@@ -1200,10 +1200,15 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
_as->load64(addr, Assembler::ScratchRegister);
_as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- // check if it's a number
- _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ScratchRegister);
- Assembler::Jump isInt = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(1));
- Assembler::Jump fallback = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
+ // check if it's integer convertible
+ _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), Assembler::ScratchRegister);
+ Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3));
+
+ // nope, not integer convertible, so check for a double:
+ _as->urshift64(Assembler::TrustedImm32(
+ QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift),
+ Assembler::ScratchRegister);
+ Assembler::Jump fallback = _as->branch32(Assembler::GreaterThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
// it's a double
_as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister);
@@ -1218,7 +1223,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *targe
generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt,
_as->loadAddress(Assembler::ScratchRegister, source));
- isInt.link(_as);
+ isIntConvertible.link(_as);
success.link(_as);
IR::Temp *targetTemp = target->asTemp();
if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) {
@@ -1790,9 +1795,9 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr
{
Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual);
- if (visitCJumpStrictNullUndefined(IR::NullType, binop, trueBlock, falseBlock))
+ if (visitCJumpStrictNull(binop, trueBlock, falseBlock))
return;
- if (visitCJumpStrictNullUndefined(IR::UndefinedType, binop, trueBlock, falseBlock))
+ if (visitCJumpStrictUndefined(binop, trueBlock, falseBlock))
return;
if (visitCJumpStrictBool(binop, trueBlock, falseBlock))
return;
@@ -1808,16 +1813,14 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr
}
// Only load the non-null temp.
-bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
+bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop,
+ IR::BasicBlock *trueBlock,
+ IR::BasicBlock *falseBlock)
{
- Q_ASSERT(nullOrUndef == IR::NullType || nullOrUndef == IR::UndefinedType);
-
IR::Expr *varSrc = 0;
- if (binop->left->type == IR::VarType && binop->right->type == nullOrUndef)
+ if (binop->left->type == IR::VarType && binop->right->type == IR::NullType)
varSrc = binop->left;
- else if (binop->left->type == nullOrUndef && binop->right->type == IR::VarType)
+ else if (binop->left->type == IR::NullType && binop->right->type == IR::VarType)
varSrc = binop->right;
if (!varSrc)
return false;
@@ -1828,7 +1831,7 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I
}
if (IR::Const *c = varSrc->asConst()) {
- if (c->type == nullOrUndef)
+ if (c->type == IR::NullType)
_as->jumpToBlock(_block, trueBlock);
else
_as->jumpToBlock(_block, falseBlock);
@@ -1841,9 +1844,54 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I
_as->load32(tagAddr, tagReg);
Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal
- : Assembler::NotEqual;
- const Assembler::TrustedImm32 tag(nullOrUndef == IR::NullType ? int(QV4::Value::Null_Type_Internal)
- : int(QV4::Value::Undefined_Type));
+ : Assembler::NotEqual;
+ const Assembler::TrustedImm32 tag(QV4::Value::Null_Type_Internal);
+ _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock);
+ return true;
+}
+
+bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop,
+ IR::BasicBlock *trueBlock,
+ IR::BasicBlock *falseBlock)
+{
+ IR::Expr *varSrc = 0;
+ if (binop->left->type == IR::VarType && binop->right->type == IR::UndefinedType)
+ varSrc = binop->left;
+ else if (binop->left->type == IR::UndefinedType && binop->right->type == IR::VarType)
+ varSrc = binop->right;
+ if (!varSrc)
+ return false;
+
+ if (varSrc->asTemp() && varSrc->asTemp()->kind == IR::Temp::PhysicalRegister) {
+ _as->jumpToBlock(_block, falseBlock);
+ return true;
+ }
+
+ if (IR::Const *c = varSrc->asConst()) {
+ if (c->type == IR::UndefinedType)
+ _as->jumpToBlock(_block, trueBlock);
+ else
+ _as->jumpToBlock(_block, falseBlock);
+ return true;
+ }
+
+ Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal
+ : Assembler::NotEqual;
+ const Assembler::RegisterID tagReg = Assembler::ScratchRegister;
+#ifdef QV4_USE_64_BIT_VALUE_ENCODING
+ Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, varSrc);
+ _as->load64(addr, tagReg);
+ const Assembler::TrustedImm64 tag(0);
+#else // !QV4_USE_64_BIT_VALUE_ENCODING
+ Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc);
+ _as->load32(tagAddr, tagReg);
+ Assembler::Jump j = _as->branch32(Assembler::invert(cond), tagReg, Assembler::TrustedImm32(0));
+ _as->addPatch(falseBlock, j);
+
+ tagAddr.offset += 4;
+ _as->load32(tagAddr, tagReg);
+ const Assembler::TrustedImm32 tag(QV4::Value::Managed_Type_Internal);
+#endif
_as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock);
return true;
}
@@ -1934,10 +1982,14 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin
if (binop->op == IR::OpNotEqual)
qSwap(trueBlock, falseBlock);
Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal)));
- Assembler::Jump isUndefined = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Undefined_Type)));
+ Assembler::Jump isNotUndefinedTag = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(int(QV4::Value::Managed_Type_Internal)));
+ tagAddr.offset -= 4;
+ _as->load32(tagAddr, tagReg);
+ Assembler::Jump isNotUndefinedValue = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(0));
_as->addPatch(trueBlock, isNull);
- _as->addPatch(trueBlock, isUndefined);
- _as->jumpToBlock(_block, falseBlock);
+ _as->addPatch(falseBlock, isNotUndefinedTag);
+ _as->addPatch(falseBlock, isNotUndefinedValue);
+ _as->jumpToBlock(_block, trueBlock);
return true;
}
diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h
index 366d510072..1e6ac1f51c 100644
--- a/src/qml/jit/qv4isel_masm_p.h
+++ b/src/qml/jit/qv4isel_masm_p.h
@@ -172,8 +172,8 @@ protected:
bool visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right,
IR::BasicBlock *iftrue, IR::BasicBlock *iffalse);
void visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
- bool visitCJumpStrictNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
+ bool visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
+ bool visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
bool visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
bool visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop,
IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock);
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index 3521673461..555eb964c1 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -184,6 +184,10 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx)
{
Scope scope(ctx);
ScopedValue arg(scope, ctx->argument(0));
+ ScopedObject instance(scope, ctx->thisObject().toObject(scope.engine));
+
+ if (!instance)
+ return ctx->d()->engine->newString()->asReturnedValue();
QString r4;
if (arg->isUndefined())
@@ -191,8 +195,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx)
else
r4 = arg->toQString();
- ScopedObject self(scope, ctx->thisObject());
- ScopedValue length(scope, self->get(ctx->d()->engine->id_length()));
+ ScopedValue length(scope, instance->get(ctx->d()->engine->id_length()));
const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32();
if (!r2)
@@ -201,7 +204,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx)
QString R;
// ### FIXME
- if (ArrayObject *a = self->as<ArrayObject>()) {
+ if (ArrayObject *a = instance->as<ArrayObject>()) {
ScopedValue e(scope);
for (uint i = 0; i < a->getLength(); ++i) {
if (i)
@@ -218,7 +221,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx)
// crazy!
//
ScopedString name(scope, ctx->d()->engine->newString(QStringLiteral("0")));
- ScopedValue r6(scope, self->get(name));
+ ScopedValue r6(scope, instance->get(name));
if (!r6->isNullOrUndefined())
R = r6->toQString();
@@ -227,7 +230,7 @@ ReturnedValue ArrayPrototype::method_join(CallContext *ctx)
R += r4;
name = Primitive::fromDouble(k).toString(scope.engine);
- r12 = self->get(name);
+ r12 = instance->get(name);
if (scope.hasException())
return Encode::undefined();
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index a6f7f8e0e7..5bc17f741b 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -315,7 +315,7 @@ struct ScopedCallData {
{
int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value);
ptr = reinterpret_cast<CallData *>(scope.alloc(size));
- ptr->tag = QV4::Value::Integer_Type;
+ ptr->tag = QV4::Value::Integer_Type_Internal;
ptr->argc = argc;
}
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index 5ce1d16911..d588553778 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -76,23 +76,85 @@ private:
/*
We use two different ways of encoding JS values. One for 32bit and one for 64bit systems.
- In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double NaN (actually -qNaN)
- is indicated by a number that has the top 13 bits set. The other values are usually set to 0 by the
- processor, and are thus free for us to store other data. We keep pointers in there for managed objects,
- and encode the other types using the free space given to use by the unused bits for NaN values. This also
- works for pointers on 64 bit systems, as they all currently only have 48 bits of addressable memory.
-
- On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value that
- will make the number a NaN. The Masks below are used for encoding the other types.
-
- On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded
- with the 13 highest bits all 0. We are now using special values for bits 14-17 to encode our values. These
- can be used, as the highest valid pointer on a 64 bit system is 2^48-1.
-
- If they are all 0, we have a pointer to a Managed object. If bit 14 is set we have an integer.
- This makes testing for pointers and numbers very fast (we have a number if any of the highest 14 bits is set).
-
- Bit 15-17 is then used to encode other immediates.
+ In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double
+ NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
+ signalling NaN it is the top 14 bits. The other values are usually set to 0 by the
+ processor, and are thus free for us to store other data. We keep pointers in there for
+ managed objects, and encode the other types using the free space given to use by the unused
+ bits for NaN values. This also works for pointers on 64 bit systems, as they all currently
+ only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for
+ pointers.)
+
+ On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value
+ that will make the number a NaN. The Masks below are used for encoding the other types.
+
+ On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will
+ get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between
+ managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave
+ the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is
+ set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are
+ used to encode Null/Int/Bool.
+
+ On both 32bit and 64bit, Undefined is encoded as a managed pointer with value 0. This is
+ the same as a nullptr.
+
+ Specific bit-sequences:
+ 0 = always 0
+ 1 = always 1
+ x = stored value
+ a,b,c,d = specific bit values, see notes
+
+ 64bit:
+
+ 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
+ 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
+ ------------------------------------------------------------------------+--------------
+ 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
+ 00000000 0000000x xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
+ a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
+ dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
+ 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
+ 00000000 00000011 10000000 00000000 00000000 00000000 00000000 00000000 | Null
+ 00000000 00000011 01000000 00000000 00000000 00000000 00000000 0000000x | Bool
+ 00000000 00000011 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
+
+ Notes:
+ - a: xor-ed signbit, always 1 for NaN
+ - bc, xor-ed values: 11 = inf, 10 = sNaN, 01 = qNaN, 00 = boxed value
+ - d: xor-ed bits, where at least one bit is set, so: (val >> (64-14)) > 0
+ - Undefined maps to C++ nullptr, so the "default" initialization is the same for both C++
+ and JS
+ - Managed has the left 15 bits set to 0, so: (val >> (64-15)) == 0
+ - empty, Null, Bool, and Int have the left 14 bits set to 0, and bit 49 set to 1,
+ so: (val >> (64-15)) == 1
+ - Null, Bool, and Int have bit 48 set, indicating integer-convertible
+ - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where
+ any non double results in a NaN
+
+ 32bit:
+
+ 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 |
+ 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value
+ ------------------------------------------------------------------------+--------------
+ 01111111 11111100 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined
+ 01111111 11111100 00000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer)
+ a1111111 1111bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf
+ xddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double
+ 01111111 11111110 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole)
+ 01111111 11111111 10000000 00000000 00000000 00000000 00000000 00000000 | Null
+ 01111111 11111111 01000000 00000000 00000000 00000000 00000000 0000000x | Bool
+ 01111111 11111111 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int
+
+ Notes:
+ - the upper 32 bits are the tag, the lower 32 bits the value
+ - Undefined has a nullptr in the value, Managed has a non-nullptr stored in the value
+ - a: sign bit, always 0 for NaN
+ - b,c: 00=inf, 01 = sNaN, 10 = qNaN, 11 = boxed value
+ - d: stored double value, as long as not *all* of them are 1, because that's a boxed value
+ (see above)
+ - empty, Null, Bool, and Int have bit 63 set to 0, bits 62-50 set to 1 (same as undefined
+ and managed), and bit 49 set to 1 (where undefined and managed have it set to 0)
+ - Null, Bool, and Int have bit 48 set, indicating integer-convertible
*/
quint64 _val;
@@ -140,7 +202,7 @@ public:
{
quint32 v;
memcpy(&v, &b, 4);
- setTagValue(Managed_Type, v);
+ setTagValue(Managed_Type_Internal, v);
}
#endif
@@ -156,12 +218,32 @@ public:
Q_ALWAYS_INLINE void setEmpty()
{
- setTagValue(Empty_Type, value());
+ setTagValue(Empty_Type_Internal, value());
}
Q_ALWAYS_INLINE void setEmpty(int i)
{
- setTagValue(Empty_Type, quint32(i));
+ setTagValue(Empty_Type_Internal, quint32(i));
+ }
+
+ enum Type {
+ Undefined_Type,
+ Managed_Type,
+ Empty_Type,
+ Integer_Type,
+ Boolean_Type,
+ Null_Type,
+ Double_Type
+ };
+
+ inline Type type() const {
+ if (isUndefined()) return Undefined_Type;
+ if (isManaged()) return Managed_Type;
+ if (isEmpty()) return Empty_Type;
+ if (isInteger()) return Integer_Type;
+ if (isBoolean()) return Boolean_Type;
+ if (isNull()) return Null_Type;
+ Q_ASSERT(isDouble()); return Double_Type;
}
#ifndef QV4_USE_64_BIT_VALUE_ENCODING
@@ -169,101 +251,64 @@ public:
SilentNaNBit = 0x00040000,
NaN_Mask = 0x7ff80000,
NotDouble_Mask = 0x7ffa0000,
- Type_Mask = 0xffffc000,
- Immediate_Mask = NotDouble_Mask | 0x00004000 | SilentNaNBit,
- IsNullOrUndefined_Mask = Immediate_Mask | 0x08000,
+ Immediate_Mask = NotDouble_Mask | 0x00020000u | SilentNaNBit,
Tag_Shift = 32
};
- enum ValueType {
- Undefined_Type = Immediate_Mask | 0x00000,
- Null_Type = Immediate_Mask | 0x10000,
- Boolean_Type = Immediate_Mask | 0x08000,
- Integer_Type = Immediate_Mask | 0x18000,
- Managed_Type = NotDouble_Mask | 0x00000 | SilentNaNBit,
- Empty_Type = NotDouble_Mask | 0x18000 | SilentNaNBit
- };
-
- enum ImmediateFlags {
- ConvertibleToInt = Immediate_Mask | 0x1
- };
-
- enum ValueTypeInternal {
- Null_Type_Internal = Null_Type | ConvertibleToInt,
- Boolean_Type_Internal = Boolean_Type | ConvertibleToInt,
- Integer_Type_Internal = Integer_Type | ConvertibleToInt,
+ enum {
+ Managed_Type_Internal = NotDouble_Mask
};
#else
- static const quint64 NaNEncodeMask = 0xffff800000000000ll;
- static const quint64 IsInt32Mask = 0x0002000000000000ll;
- static const quint64 IsDoubleMask = 0xfffc000000000000ll;
- static const quint64 IsNumberMask = IsInt32Mask|IsDoubleMask;
- static const quint64 IsNullOrUndefinedMask = 0x0000800000000000ll;
- static const quint64 IsNullOrBooleanMask = 0x0001000000000000ll;
- static const quint64 IsConvertibleToIntMask = IsInt32Mask|IsNullOrBooleanMask;
+ static const quint64 NaNEncodeMask = 0xfffc000000000000ll;
+ static const quint64 Immediate_Mask = 0x00020000u; // bit 49
enum Masks {
NaN_Mask = 0x7ff80000,
- Type_Mask = 0xffff8000,
- IsDouble_Mask = 0xfffc0000,
- Immediate_Mask = 0x00018000,
- IsNullOrUndefined_Mask = 0x00008000,
- IsNullOrBoolean_Mask = 0x00010000,
- Tag_Shift = 32
- };
- enum ValueType {
- Undefined_Type = IsNullOrUndefined_Mask,
- Null_Type = IsNullOrUndefined_Mask|IsNullOrBoolean_Mask,
- Boolean_Type = IsNullOrBoolean_Mask,
- Integer_Type = 0x20000|IsNullOrBoolean_Mask,
- Managed_Type = 0,
- Empty_Type = Undefined_Type | 0x4000
};
enum {
IsDouble_Shift = 64-14,
- IsNumber_Shift = 64-15,
- IsConvertibleToInt_Shift = 64-16,
- IsManaged_Shift = 64-17
+ IsManagedOrUndefined_Shift = 64-15,
+ IsIntegerConvertible_Shift = 64-16,
+ Tag_Shift = 32,
+ IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift,
+ Managed_Type_Internal = 0
};
-
-
+#endif
enum ValueTypeInternal {
- Null_Type_Internal = Null_Type,
- Boolean_Type_Internal = Boolean_Type,
- Integer_Type_Internal = Integer_Type
+ Empty_Type_Internal = Immediate_Mask | 0,
+ ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48
+ Null_Type_Internal = ConvertibleToInt | 0x08000u,
+ Boolean_Type_Internal = ConvertibleToInt | 0x04000u,
+ Integer_Type_Internal = ConvertibleToInt | 0x02000u
};
-#endif
-
- inline unsigned type() const {
- return tag() & Type_Mask;
- }
// used internally in property
- inline bool isEmpty() const { return tag() == Empty_Type; }
-
- inline bool isUndefined() const { return tag() == Undefined_Type; }
+ inline bool isEmpty() const { return tag() == Empty_Type_Internal; }
inline bool isNull() const { return tag() == Null_Type_Internal; }
- inline bool isBoolean() const { return tag ()== Boolean_Type_Internal; }
+ inline bool isBoolean() const { return tag() == Boolean_Type_Internal; }
+ inline bool isInteger() const { return tag() == Integer_Type_Internal; }
+ inline bool isNullOrUndefined() const { return isNull() || isUndefined(); }
+ inline bool isNumber() const { return isDouble() || isInteger(); }
+
#ifdef QV4_USE_64_BIT_VALUE_ENCODING
- inline bool isInteger() const { return (_val >> IsNumber_Shift) == 1; }
+ inline bool isUndefined() const { return _val == 0; }
inline bool isDouble() const { return (_val >> IsDouble_Shift); }
- inline bool isNumber() const { return (_val >> IsNumber_Shift); }
- inline bool isManaged() const { return !(_val >> IsManaged_Shift); }
- inline bool isNullOrUndefined() const { return ((_val >> IsManaged_Shift) & ~2) == 1; }
- inline bool integerCompatible() const { return ((_val >> IsConvertibleToInt_Shift) & ~2) == 1; }
+ inline bool isManaged() const { return !isUndefined() && ((_val >> IsManagedOrUndefined_Shift) == 0); }
+
+ inline bool integerCompatible() const {
+ return (_val >> IsIntegerConvertible_Shift) == 3;
+ }
static inline bool integerCompatible(Value a, Value b) {
return a.integerCompatible() && b.integerCompatible();
}
static inline bool bothDouble(Value a, Value b) {
return a.isDouble() && b.isDouble();
}
- inline bool isNaN() const { return (tag() & 0x7fff8000) == 0x00078000; }
+ inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; }
#else
- inline bool isInteger() const { return tag() == Integer_Type_Internal; }
+ inline bool isUndefined() const { return tag() == Managed_Type_Internal && value() == 0; }
inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; }
- inline bool isNumber() const { return tag() == Integer_Type_Internal || (tag() & NotDouble_Mask) != NotDouble_Mask; }
- inline bool isManaged() const { return tag() == Managed_Type; }
- inline bool isNullOrUndefined() const { return (tag() & IsNullOrUndefined_Mask) == Undefined_Type; }
+ inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); }
inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; }
static inline bool integerCompatible(Value a, Value b) {
return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt;
@@ -503,14 +548,14 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value
inline Primitive Primitive::undefinedValue()
{
Primitive v;
- v.setTagValue(Undefined_Type, 0);
+ v.setM(Q_NULLPTR);
return v;
}
inline Primitive Primitive::emptyValue()
{
Primitive v;
- v.setTagValue(Value::Empty_Type, 0);
+ v.setEmpty(0);
return v;
}
@@ -538,7 +583,6 @@ inline Primitive Primitive::fromDouble(double d)
inline Primitive Primitive::fromInt32(int i)
{
Primitive v;
- v.setTagValue(Integer_Type_Internal, 0);
v.setInt_32(i);
return v;
}
@@ -556,31 +600,23 @@ inline Primitive Primitive::fromUInt32(uint i)
struct Encode {
static ReturnedValue undefined() {
- return quint64(Value::Undefined_Type) << Value::Tag_Shift;
+ return Primitive::undefinedValue().rawValue();
}
static ReturnedValue null() {
- return quint64(Value::Null_Type_Internal) << Value::Tag_Shift;
+ return Primitive::nullValue().rawValue();
}
Encode(bool b) {
- val = (quint64(Value::Boolean_Type_Internal) << Value::Tag_Shift) | (uint)b;
+ val = Primitive::fromBoolean(b).rawValue();
}
Encode(double d) {
- Value v;
- v.setDouble(d);
- val = v.rawValue();
+ val = Primitive::fromDouble(d).rawValue();
}
Encode(int i) {
- val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | (uint)i;
+ val = Primitive::fromInt32(i).rawValue();
}
Encode(uint i) {
- if (i <= INT_MAX) {
- val = (quint64(Value::Integer_Type_Internal) << Value::Tag_Shift) | i;
- } else {
- Value v;
- v.setDouble(i);
- val = v.rawValue();
- }
+ val = Primitive::fromUInt32(i).rawValue();
}
Encode(ReturnedValue v) {
val = v;
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index ecbc243baa..fbb26dc571 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -569,7 +569,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
#endif // DO_TRACE_INSTR
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
STOREVALUE(instr.result, Runtime::callValue(engine, VALUE(instr.dest), callData));
@@ -579,7 +579,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::callProperty(engine, instr.name, callData));
@@ -588,7 +588,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CallPropertyLookup)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::callPropertyLookup(engine, instr.lookupIndex, callData));
@@ -598,7 +598,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::callQmlScopeObjectProperty(engine, instr.index, callData));
@@ -608,7 +608,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::callQmlContextObjectProperty(engine, instr.index, callData));
@@ -617,7 +617,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CallElement)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::callElement(engine, VALUE(instr.index), callData));
@@ -626,7 +626,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CallActivationProperty)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
STOREVALUE(instr.result, Runtime::callActivationProperty(engine, instr.name, callData));
@@ -635,7 +635,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CallGlobalLookup)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
STOREVALUE(instr.result, Runtime::callGlobalLookup(engine, instr.index, callData));
@@ -741,7 +741,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CreateValue)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
STOREVALUE(instr.result, Runtime::constructValue(engine, VALUE(instr.func), callData));
@@ -750,7 +750,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CreateProperty)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::constructProperty(engine, instr.name, callData));
@@ -759,7 +759,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(ConstructPropertyLookup)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = VALUE(instr.base);
STOREVALUE(instr.result, Runtime::constructPropertyLookup(engine, instr.index, callData));
@@ -768,7 +768,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CreateActivationProperty)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
STOREVALUE(instr.result, Runtime::constructActivationProperty(engine, instr.name, callData));
@@ -777,7 +777,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(ConstructGlobalLookup)
Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
- callData->tag = QV4::Value::Integer_Type;
+ callData->tag = QV4::Value::Integer_Type_Internal;
callData->argc = instr.argc;
callData->thisObject = QV4::Primitive::undefinedValue();
STOREVALUE(instr.result, Runtime::constructGlobalLookup(engine, instr.index, callData));
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 77c7ae106b..6207a86877 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -41,6 +41,7 @@
#include "qquicksprite_p.h"
#include "qquickspriteengine_p.h"
#include <QtQuick/private/qsgcontext_p.h>
+#include <QtQuick/private/qquickitem_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <private/qqmlglobal_p.h>
#include <QtQuick/qsgnode.h>
@@ -368,7 +369,7 @@ void QQuickAnimatedSprite::start()
}
emit currentFrameChanged(0);
emit runningChanged(true);
- update();
+ maybeUpdate();
}
void QQuickAnimatedSprite::stop()
@@ -378,7 +379,7 @@ void QQuickAnimatedSprite::stop()
return;
m_pauseOffset = 0;
emit runningChanged(false);
- update();
+ maybeUpdate();
}
/*!
@@ -396,7 +397,15 @@ void QQuickAnimatedSprite::advance(int frames)
m_curFrame += m_spriteEngine->maxFrames();
m_curFrame = m_curFrame % m_spriteEngine->maxFrames();
emit currentFrameChanged(m_curFrame);
- update();
+ maybeUpdate();
+}
+
+void QQuickAnimatedSprite::maybeUpdate()
+{
+ QQuickItemPrivate *priv = QQuickItemPrivate::get(this);
+ const QLazilyAllocated<QQuickItemPrivate::ExtraData> &extraData = priv->extra;
+ if ((extraData.isAllocated() && extraData->effectRefCount > 0) || priv->effectiveVisible)
+ update();
}
/*!
@@ -414,7 +423,7 @@ void QQuickAnimatedSprite::pause()
m_pauseOffset = m_timestamp.elapsed();
m_paused = true;
emit pausedChanged(true);
- update();
+ maybeUpdate();
}
/*!
@@ -432,7 +441,7 @@ void QQuickAnimatedSprite::resume()
m_pauseOffset = m_pauseOffset - m_timestamp.elapsed();
m_paused = false;
emit pausedChanged(false);
- update();
+ maybeUpdate();
}
void QQuickAnimatedSprite::createEngine()
@@ -444,7 +453,6 @@ void QQuickAnimatedSprite::createEngine()
m_spriteEngine = new QQuickSpriteEngine(QList<QQuickSprite*>(spriteList), this);
m_spriteEngine->startAssemblingImage();
reset();
- update();
}
static QSGGeometry::Attribute AnimatedSprite_Attributes[] = {
@@ -482,10 +490,10 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode()
return 0;
} else if (m_spriteEngine->status() == QQuickPixmap::Null) {
m_spriteEngine->startAssemblingImage();
- update();//Schedule another update, where we will check again
+ maybeUpdate();//Schedule another update, where we will check again
return 0;
} else if (m_spriteEngine->status() == QQuickPixmap::Loading) {
- update();//Schedule another update, where we will check again
+ maybeUpdate();//Schedule another update, where we will check again
return 0;
}
@@ -547,7 +555,7 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode()
void QQuickAnimatedSprite::reset()
{
m_pleaseReset = true;
- update();
+ maybeUpdate();
}
QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
@@ -568,7 +576,7 @@ QSGNode *QQuickAnimatedSprite::updatePaintNode(QSGNode *oldNode, UpdatePaintNode
if (m_running) {
if (!m_paused)
- update();
+ maybeUpdate();
if (node) {
node->markDirty(QSGNode::DirtyMaterial);
@@ -624,7 +632,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
frameAt = 0;
m_running = false;
emit runningChanged(false);
- update();
+ maybeUpdate();
}
} else {
frameAt = m_curFrame;
@@ -632,7 +640,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGGeometryNode *node)
if (m_curFrame != lastFrame) {
if (isCurrentFrameChangedConnected())
emit currentFrameChanged(m_curFrame);
- update();
+ maybeUpdate();
}
qreal frameCount = m_spriteEngine->spriteFrames();
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index 1e5981c1a4..9d8b4dad40 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -366,6 +366,7 @@ protected:
void componentComplete() Q_DECL_OVERRIDE;
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
private:
+ void maybeUpdate();
bool isCurrentFrameChangedConnected();
void prepareNextFrame(QSGGeometryNode *node);
void reloadImage();
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index c838eae3c7..49117d27d1 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -1573,7 +1573,7 @@ void QQuickFlickable::timerEvent(QTimerEvent *event)
d->movementEndingTimer.stop();
d->pressed = false;
d->stealMouse = false;
- if (!d->velocityTimeline.isActive())
+ if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
movementEnding(true, true);
}
}
@@ -1626,7 +1626,7 @@ void QQuickFlickable::viewportMoved(Qt::Orientations orient)
void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
QQuickTimeLineCallback::Callback fixupCallback)
{
- if (pressed || calcVelocity) {
+ if (!scrollingPhase && (pressed || calcVelocity)) {
int elapsed = data.velocityTime.restart();
if (elapsed > 0) {
qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp
index 234105986a..609666d32b 100644
--- a/src/quick/items/qquickmousearea.cpp
+++ b/src/quick/items/qquickmousearea.cpp
@@ -672,6 +672,7 @@ void QQuickMouseArea::mousePressEvent(QMouseEvent *event)
Q_D(QQuickMouseArea);
d->moved = false;
d->stealMouse = d->preventStealing;
+ d->overThreshold = false;
if (!d->enabled || !(event->button() & acceptedMouseButtons())) {
QQuickItem::mousePressEvent(event);
} else {
@@ -952,6 +953,7 @@ bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event)
if (!d->pressed) {
// no other buttons are pressed
d->stealMouse = false;
+ d->overThreshold = false;
if (c && c->mouseGrabberItem() == this)
ungrabMouse();
emit canceled();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 8288554028..80321cf5d1 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -897,11 +897,11 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
// If the width of the item has changed and it's possible the result of wrapping,
// eliding, scaling has changed, or the text is not left aligned do another layout.
- if ((lineWidth < qMin(oldWidth, naturalWidth) || (widthExceeded && lineWidth > oldWidth))
+ if ((!qFuzzyCompare(lineWidth, oldWidth) || (widthExceeded && lineWidth > oldWidth))
&& (singlelineElide || multilineElide || canWrap || horizontalFit
|| q->effectiveHAlign() != QQuickText::AlignLeft)) {
widthChanged = true;
- widthExceeded = false;
+ widthExceeded = lineWidth >= qMin(oldWidth, naturalWidth);
heightExceeded = false;
continue;
}
@@ -936,7 +936,7 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
bool wasInLayout = internalWidthUpdate;
internalWidthUpdate = true;
- q->setImplicitHeight(naturalHeight);
+ q->setImplicitHeight(naturalHeight + q->topPadding() + q->bottomPadding());
internalWidthUpdate = wasInLayout;
multilineElide = elideMode == QQuickText::ElideRight
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 0ad6b43be6..842afe9dff 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -627,12 +627,12 @@ void QSGRenderContext::initialize(QOpenGLContext *context)
#ifdef Q_OS_LINUX
const char *vendor = (const char *) funcs->glGetString(GL_VENDOR);
- if (strstr(vendor, "nouveau"))
+ if (vendor && strstr(vendor, "nouveau"))
m_brokenIBOs = true;
const char *renderer = (const char *) funcs->glGetString(GL_RENDERER);
- if (strstr(renderer, "llvmpipe"))
+ if (renderer && strstr(renderer, "llvmpipe"))
m_serializedRender = true;
- if (strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
+ if (vendor && renderer && strstr(vendor, "Hisilicon Technologies") && strstr(renderer, "Immersion.16"))
m_brokenIBOs = true;
#endif
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 0001a22103..13bfa16581 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -138,6 +138,7 @@ private slots:
void arrayPop_QTBUG_35979();
void array_unshift_QTBUG_52065();
+ void array_join_QTBUG_53672();
void regexpLastMatch();
void indexedAccesses();
@@ -3020,6 +3021,14 @@ void tst_QJSEngine::array_unshift_QTBUG_52065()
QCOMPARE(result.property(i).toInt(), i);
}
+void tst_QJSEngine::array_join_QTBUG_53672()
+{
+ QJSEngine eng;
+ QJSValue result = eng.evaluate("Array.prototype.join.call(0)");
+ QVERIFY(result.isString());
+ QCOMPARE(result.toString(), QString(""));
+}
+
void tst_QJSEngine::regexpLastMatch()
{
QJSEngine eng;
diff --git a/tests/auto/quick/qquickflickable/data/wheel.qml b/tests/auto/quick/qquickflickable/data/wheel.qml
index 2928bbcd72..2be543cdde 100644
--- a/tests/auto/quick/qquickflickable/data/wheel.qml
+++ b/tests/auto/quick/qquickflickable/data/wheel.qml
@@ -8,9 +8,14 @@ Rectangle {
Flickable {
id: flick
objectName: "flick"
+ property bool ended: false
+ property int movementsAfterEnd: 0
anchors.fill: parent
contentWidth: 800
contentHeight: 800
+ onContentXChanged: if (ended) ++movementsAfterEnd
+ onContentYChanged: if (ended) ++movementsAfterEnd
+ onMovementEnded: ended = true
Rectangle {
width: flick.contentWidth
diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
index bbaccfe534..339e8946e8 100644
--- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
+++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp
@@ -741,7 +741,10 @@ void tst_qquickflickable::wheel()
QQuickFlickable *flick = window->rootObject()->findChild<QQuickFlickable*>("flick");
QVERIFY(flick != 0);
+ QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flick);
+ QSignalSpy moveEndSpy(flick, SIGNAL(movementEnded()));
+ // test a vertical flick
{
QPoint pos(200, 200);
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier);
@@ -752,9 +755,19 @@ void tst_qquickflickable::wheel()
QTRY_VERIFY(flick->contentY() > 0);
QCOMPARE(flick->contentX(), qreal(0));
- flick->setContentY(0);
+ QTRY_COMPARE(moveEndSpy.count(), 1);
+ QCOMPARE(fp->velocityTimeline.isActive(), false);
+ QCOMPARE(fp->timeline.isActive(), false);
+ QTest::qWait(50); // make sure that onContentYChanged won't sneak in again
+ QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
+
+ // get ready to test horizontal flick
+ flick->setContentY(0); // which triggers movementEnded again
+ flick->setProperty("movementsAfterEnd", 0);
+ flick->setProperty("ended", false);
QCOMPARE(flick->contentY(), qreal(0));
+ // test a horizontal flick
{
QPoint pos(200, 200);
QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier);
@@ -765,6 +778,11 @@ void tst_qquickflickable::wheel()
QTRY_VERIFY(flick->contentX() > 0);
QCOMPARE(flick->contentY(), qreal(0));
+ QTRY_COMPARE(moveEndSpy.count(), 2);
+ QCOMPARE(fp->velocityTimeline.isActive(), false);
+ QCOMPARE(fp->timeline.isActive(), false);
+ QTest::qWait(50); // make sure that onContentXChanged won't sneak in again
+ QCOMPARE(flick->property("movementsAfterEnd").value<int>(), 0); // QTBUG-55886
}
void tst_qquickflickable::movingAndFlicking_data()
diff --git a/tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml b/tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml
new file mode 100644
index 0000000000..0d5b496766
--- /dev/null
+++ b/tests/auto/quick/qquickmousearea/data/nestedFlickableStopAtBounds.qml
@@ -0,0 +1,44 @@
+import QtQuick 2.5
+
+
+Rectangle {
+ width: 240
+ height: 320
+
+ MouseArea {
+ objectName: "mouseArea"
+ anchors.fill: parent
+
+ drag.target: moveable
+ drag.filterChildren: true
+
+ Rectangle {
+ id: moveable
+ objectName: "moveable"
+ color: "red"
+ x: 50
+ y: 80
+ width: 200
+ height: 200
+ }
+
+ Flickable {
+ objectName: "flickable"
+ anchors.fill: parent
+ contentHeight: 450
+
+ Rectangle {
+ x: 100
+ y: 50
+ width: 50
+ height: 350
+ color: "yellow"
+ }
+
+ MouseArea {
+ width: 240
+ height: 450
+ }
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
index 89d48c4f09..5891e67df6 100644
--- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
+++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp
@@ -120,6 +120,7 @@ private slots:
void moveAndReleaseWithoutPress();
void nestedStopAtBounds();
void nestedStopAtBounds_data();
+ void nestedFlickableStopAtBounds();
void containsPress_data();
void containsPress();
void ignoreBySource();
@@ -1751,6 +1752,98 @@ void tst_QQuickMouseArea::nestedStopAtBounds()
QTest::mouseRelease(&view, Qt::LeftButton, 0, position);
}
+void tst_QQuickMouseArea::nestedFlickableStopAtBounds()
+{
+ QQuickView view;
+ QByteArray errorMessage;
+ QVERIFY2(initView(view, testFileUrl("nestedFlickableStopAtBounds.qml"), false, &errorMessage), errorMessage.constData());
+ view.show();
+ view.requestActivate();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QVERIFY(view.rootObject());
+
+ QQuickMouseArea *mouseArea = view.rootObject()->findChild<QQuickMouseArea*>("mouseArea");
+ QVERIFY(mouseArea);
+
+ QQuickFlickable *flickable = mouseArea->findChild<QQuickFlickable*>("flickable");
+ QVERIFY(flickable);
+
+ const int threshold = qApp->styleHints()->startDragDistance();
+
+ QPoint position(200, 280);
+ int &pos = position.ry();
+
+ // Drag up - should move the Flickable to end
+ QTest::mousePress(&view, Qt::LeftButton, 0, position);
+ QTest::qWait(10);
+ pos -= threshold * 2;
+ QTest::mouseMove(&view, position);
+ pos -= threshold * 2;
+ QTest::mouseMove(&view, position);
+ QTest::qWait(10);
+ pos -= 150;
+ QTest::mouseMove(&view, position);
+ QVERIFY(flickable->isDragging());
+ QVERIFY(!mouseArea->drag()->active());
+ QCOMPARE(flickable->isAtYEnd(), true);
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, position);
+
+ QTRY_VERIFY(!flickable->isMoving());
+
+ pos = 280;
+
+ // Drag up again - should activate MouseArea drag
+ QVERIFY(!mouseArea->drag()->active());
+ QTest::mousePress(&view, Qt::LeftButton, 0, position);
+ QTest::qWait(10);
+ pos -= threshold * 2;
+ QTest::mouseMove(&view, position);
+ pos -= threshold * 2;
+ QTest::mouseMove(&view, position);
+ QTest::qWait(10);
+ pos -= 20;
+ QTest::mouseMove(&view, position);
+ QVERIFY(mouseArea->drag()->active());
+ QCOMPARE(flickable->isAtYEnd(), true);
+ QVERIFY(!flickable->isDragging());
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, position);
+
+ // Drag to the top and verify that the MouseArea doesn't steal the grab when we drag back (QTBUG-56036)
+ pos = 50;
+
+ QTest::mousePress(&view, Qt::LeftButton, 0, position);
+ QTest::qWait(10);
+ pos += threshold;
+ QTest::mouseMove(&view, position);
+ pos += threshold;
+ QTest::mouseMove(&view, position);
+ QTest::qWait(10);
+ pos += 150;
+ QTest::mouseMove(&view, position);
+ QVERIFY(flickable->isDragging());
+ QVERIFY(!mouseArea->drag()->active());
+ QCOMPARE(flickable->isAtYBeginning(), true);
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, position);
+
+ QTRY_VERIFY(!flickable->isMoving());
+
+ pos = 280;
+
+ // Drag up again - should not activate MouseArea drag
+ QTest::mousePress(&view, Qt::LeftButton, 0, position);
+ QTest::qWait(10);
+ pos -= threshold;
+ QTest::mouseMove(&view, position);
+ pos -= threshold;
+ QTest::mouseMove(&view, position);
+ QTest::qWait(10);
+ pos -= 100;
+ QTest::mouseMove(&view, position);
+ QVERIFY(flickable->isDragging());
+ QVERIFY(!mouseArea->drag()->active());
+ QTest::mouseRelease(&view, Qt::LeftButton, 0, position);
+}
+
void tst_QQuickMouseArea::containsPress_data()
{
QTest::addColumn<bool>("hoverEnabled");
diff --git a/tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml b/tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml
new file mode 100644
index 0000000000..4358a58f57
--- /dev/null
+++ b/tests/auto/quick/qquicktext/data/hAlignWidthDependsOnImplicitWidth.qml
@@ -0,0 +1,25 @@
+import QtQuick 2.6
+
+Item {
+ width: 200
+ height: 200
+
+ property alias text: label.text
+ property alias horizontalAlignment: label.horizontalAlignment
+ property alias elide: label.elide
+ property int extraWidth: 0
+
+ Rectangle {
+ border.color: "red"
+ x: 100
+ width: label.implicitWidth + extraWidth
+ height: label.implicitHeight
+
+ Text {
+ id: label
+ anchors.fill: parent
+ text: 'press me'
+ horizontalAlignment: Text.AlignHCenter
+ }
+ }
+}
diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
index 5ee811fd37..f31859ee49 100644
--- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp
+++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp
@@ -150,6 +150,9 @@ private slots:
void zeroWidthAndElidedDoesntRender();
+ void hAlignWidthDependsOnImplicitWidth_data();
+ void hAlignWidthDependsOnImplicitWidth();
+
private:
QStringList standard;
QStringList richText;
@@ -4114,6 +4117,18 @@ void tst_qquicktext::padding()
QCOMPARE(obj->bottomPadding(), 1.11);
QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
+ obj->setWidth(cw / 2);
+ obj->setElideMode(QQuickText::ElideRight);
+ QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
+ QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
+ obj->setElideMode(QQuickText::ElideNone);
+ obj->resetWidth();
+
+ obj->setWrapMode(QQuickText::WordWrap);
+ QCOMPARE(obj->implicitWidth(), cw + obj->leftPadding() + obj->rightPadding());
+ QCOMPARE(obj->implicitHeight(), ch + obj->topPadding() + obj->bottomPadding());
+ obj->setWrapMode(QQuickText::NoWrap);
+
obj->setText("Qt");
QVERIFY(obj->contentWidth() < cw);
QCOMPARE(obj->contentHeight(), ch);
@@ -4170,6 +4185,51 @@ void tst_qquicktext::zeroWidthAndElidedDoesntRender()
QCOMPARE(text->contentWidth(), reference->contentWidth());
}
+void tst_qquicktext::hAlignWidthDependsOnImplicitWidth_data()
+{
+ QTest::addColumn<QQuickText::HAlignment>("horizontalAlignment");
+ QTest::addColumn<QQuickText::TextElideMode>("elide");
+ QTest::addColumn<int>("extraWidth");
+
+ QTest::newRow("AlignHCenter, ElideNone, 0 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideNone << 0;
+ QTest::newRow("AlignRight, ElideNone, 0 extraWidth") << QQuickText::AlignRight << QQuickText::ElideNone << 0;
+ QTest::newRow("AlignHCenter, ElideRight, 0 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideRight << 0;
+ QTest::newRow("AlignRight, ElideRight, 0 extraWidth") << QQuickText::AlignRight << QQuickText::ElideRight << 0;
+ QTest::newRow("AlignHCenter, ElideNone, 20 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideNone << 20;
+ QTest::newRow("AlignRight, ElideNone, 20 extraWidth") << QQuickText::AlignRight << QQuickText::ElideNone << 20;
+ QTest::newRow("AlignHCenter, ElideRight, 20 extraWidth") << QQuickText::AlignHCenter << QQuickText::ElideRight << 20;
+ QTest::newRow("AlignRight, ElideRight, 20 extraWidth") << QQuickText::AlignRight << QQuickText::ElideRight << 20;
+}
+
+void tst_qquicktext::hAlignWidthDependsOnImplicitWidth()
+{
+ QFETCH(QQuickText::HAlignment, horizontalAlignment);
+ QFETCH(QQuickText::TextElideMode, elide);
+ QFETCH(int, extraWidth);
+
+ QScopedPointer<QQuickView> window(new QQuickView);
+ window->setSource(testFileUrl("hAlignWidthDependsOnImplicitWidth.qml"));
+ QTRY_COMPARE(window->status(), QQuickView::Ready);
+
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+
+ QQuickItem *rect = window->rootObject();
+ QVERIFY(rect);
+
+ QVERIFY(rect->setProperty("horizontalAlignment", horizontalAlignment));
+ QVERIFY(rect->setProperty("elide", elide));
+ QVERIFY(rect->setProperty("extraWidth", extraWidth));
+
+ QImage image = window->grabWindow();
+ const int rectX = 100 * window->screen()->devicePixelRatio();
+ QCOMPARE(numberOfNonWhitePixels(0, rectX - 1, image), 0);
+
+ QVERIFY(rect->setProperty("text", "this is mis-aligned"));
+ image = window->grabWindow();
+ QCOMPARE(numberOfNonWhitePixels(0, rectX - 1, image), 0);
+}
+
QTEST_MAIN(tst_qquicktext)
#include "tst_qquicktext.moc"