aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/common/qv4compileddata_p.h5
-rw-r--r--src/qml/compiler/qv4compiler.cpp18
-rw-r--r--src/qml/jsruntime/qv4function_p.h1
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp31
-rw-r--r--tests/auto/qml/qqmllanguage/data/thisInArrow.qml39
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp25
6 files changed, 107 insertions, 12 deletions
diff --git a/src/qml/common/qv4compileddata_p.h b/src/qml/common/qv4compileddata_p.h
index 00a1890bf6..30d85ce8b9 100644
--- a/src/qml/common/qv4compileddata_p.h
+++ b/src/qml/common/qv4compileddata_p.h
@@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE
// Also change the comment behind the number to describe the latest change. This has the added
// benefit that if another patch changes the version too, it will result in a merge conflict, and
// not get removed silently.
-#define QV4_DATA_STRUCTURE_VERSION 0x33 // added new bytecode instruction for type assertions
+#define QV4_DATA_STRUCTURE_VERSION 0x34 // added a flag to mark functions as closure wrappers
class QIODevice;
class QQmlTypeNameCache;
@@ -287,7 +287,8 @@ struct Function
enum Flags : unsigned int {
IsStrict = 0x1,
IsArrowFunction = 0x2,
- IsGenerator = 0x4
+ IsGenerator = 0x4,
+ IsClosureWrapper = 0x8,
};
// Absolute offset into file where the code for this function is located.
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index 5511ed304a..02e2470850 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -432,9 +432,21 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
function->flags |= CompiledData::Function::IsArrowFunction;
if (irFunction->isGenerator)
function->flags |= CompiledData::Function::IsGenerator;
- function->nestedFunctionIndex =
- irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first()))
- : std::numeric_limits<uint32_t>::max();
+ if (irFunction->returnsClosure)
+ function->flags |= CompiledData::Function::IsClosureWrapper;
+
+ if (!irFunction->returnsClosure
+ || irFunction->innerFunctionAccessesThis
+ || irFunction->innerFunctionAccessesNewTarget) {
+ // If the inner function does things with this and new.target we need to do some work in
+ // the outer function. Then we shouldn't directly access the nested function.
+ function->nestedFunctionIndex = std::numeric_limits<uint32_t>::max();
+ } else {
+ // Otherwise we can directly use the nested function.
+ function->nestedFunctionIndex
+ = quint32(module->functions.indexOf(irFunction->nestedContexts.first()));
+ }
+
function->length = irFunction->formals ? irFunction->formals->length() : 0;
function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 7577161c01..20afeff140 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -138,6 +138,7 @@ public:
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; }
inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; }
+ inline bool isClosureWrapper() const { return compiledFunction->flags & CompiledData::Function::IsClosureWrapper; }
QQmlSourceLocation sourceLocation() const;
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index 665d4e6c75..13ed3cf95b 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -114,12 +114,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int
QV4::ExecutionEngine *engine = ctxt->engine()->handle();
- // If the function is marked as having a nested function, then the user wrote:
- // onSomeSignal: function() { /*....*/ }
- // So take that nested function:
- if (auto closure = function->nestedFunction()) {
- function = closure;
- } else {
+ if (!function->isClosureWrapper()) {
QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames();
if (!signalParameters.isEmpty()) {
QString error;
@@ -136,7 +131,29 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(const QObject *target, int
QV4::Scoped<QV4::QmlContext> qmlContext(valueScope, scope);
if (!qmlContext)
qmlContext = QV4::QmlContext::create(engine->rootContext(), ctxt, scopeObject);
- setupFunction(qmlContext, function);
+ if (auto closure = function->nestedFunction()) {
+ // If the function is marked as having a nested function, then the user wrote:
+ // onSomeSignal: function() { /*....*/ }
+ // So take that nested function:
+ setupFunction(qmlContext, closure);
+ } else {
+ setupFunction(qmlContext, function);
+
+ // If it's a closure wrapper but we cannot directly access the nested function
+ // we need to run the outer function to get the nested one.
+ if (function->isClosureWrapper()) {
+ bool isUndefined = false;
+ QV4::ScopedFunctionObject result(
+ valueScope, QQmlJavaScriptExpression::evaluate(&isUndefined));
+
+ Q_ASSERT(!isUndefined);
+ Q_ASSERT(result->function());
+ Q_ASSERT(result->function()->compilationUnit == function->compilationUnit);
+
+ QV4::Scoped<QV4::ExecutionContext> callContext(valueScope, result->scope());
+ setupFunction(callContext, result->function());
+ }
+ }
}
void QQmlBoundSignalExpression::init(const QQmlRefPointer<QQmlContextData> &ctxt, QObject *scope)
diff --git a/tests/auto/qml/qqmllanguage/data/thisInArrow.qml b/tests/auto/qml/qqmllanguage/data/thisInArrow.qml
new file mode 100644
index 0000000000..7dd19782e6
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/thisInArrow.qml
@@ -0,0 +1,39 @@
+import QtQml
+
+QtObject {
+ id: root
+
+ property int width: 43
+ Component.onCompleted: () => { console.log(this.width); }
+
+ property var arrow: () => { return this; }
+ property var func: function() { return this; }
+
+ property QtObject child: QtObject {
+ property var aa;
+ property var ff;
+
+ Component.onCompleted: {
+ root.arrowResult = root.arrow();
+ root.funcResult = root.func();
+
+ var a = root.arrow;
+ root.aResult = a();
+ var f = root.func;
+ root.fResult = f();
+
+ aa = a;
+ root.aaResult = aa();
+
+ ff = f;
+ root.ffResult = ff();
+ }
+ }
+
+ property var arrowResult
+ property var funcResult
+ property var aResult
+ property var fResult
+ property var aaResult
+ property var ffResult
+}
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index d09f903ccb..bf7953c51c 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -364,6 +364,7 @@ private slots:
void propertyObserverOnReadonly();
void variantListConversion();
+ void thisInArrowFunction();
private:
QQmlEngine engine;
@@ -6372,6 +6373,30 @@ void tst_qqmllanguage::variantListConversion()
QCOMPARE(l1.a, 13ull);
}
+void tst_qqmllanguage::thisInArrowFunction()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("thisInArrow.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ QTest::ignoreMessage(QtDebugMsg, "43");
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+
+ QCOMPARE(qvariant_cast<QObject *>(o->property("arrowResult")), o.data());
+ QCOMPARE(qvariant_cast<QObject *>(o->property("funcResult")), o.data());
+ QCOMPARE(qvariant_cast<QObject *>(o->property("aResult")), o.data());
+ QCOMPARE(qvariant_cast<QObject *>(o->property("aaResult")), o.data());
+
+ QCOMPARE(qvariant_cast<QObject *>(o->property("fResult")), nullptr);
+ QCOMPARE(o->property("fResult").metaType(), QMetaType::fromType<QJSValue>());
+ QVERIFY(qvariant_cast<QJSValue>(o->property("fResult")).isObject());
+
+ QObject *child = qvariant_cast<QObject *>(o->property("child"));
+ QVERIFY(child != nullptr);
+ QCOMPARE(qvariant_cast<QObject *>(o->property("ffResult")), child);
+}
+
QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc"