aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp47
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp5
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp4
-rw-r--r--src/qml/qml/qqmlbinding.cpp48
-rw-r--r--src/qml/qml/qqmlbinding_p.h11
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp14
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h4
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp8
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp9
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h8
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp13
-rw-r--r--tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml34
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp12
14 files changed, 176 insertions, 43 deletions
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index a8c1640767..dc8ee550d5 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -75,7 +75,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
jsConstruct = QV4::FunctionObject::callAsConstructor;
Object::init();
- function = nullptr;
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
@@ -88,7 +87,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
Object::init();
- function = nullptr;
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
@@ -101,8 +99,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
Object::init();
- this->function = function;
- function->compilationUnit->addref();
+ setFunction(function);
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedString name(s, function->name());
@@ -123,13 +120,18 @@ void Heap::FunctionObject::init()
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
Object::init();
- function = nullptr;
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue());
}
-
+void Heap::FunctionObject::setFunction(Function *f)
+{
+ if (f) {
+ function = f;
+ function->compilationUnit->addref();
+ }
+}
void Heap::FunctionObject::destroy()
{
if (function)
@@ -347,20 +349,36 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
{
QV4::Scope scope(b);
ScopedFunctionObject target(scope, thisObject);
- if (!target)
+ if (!target || target->isBinding())
return scope.engine->throwTypeError();
ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue());
Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)nullptr);
- if (argc > 1) {
- boundArgs = MemberData::allocate(scope.engine, argc - 1);
- boundArgs->d()->values.size = argc - 1;
- for (uint i = 0, ei = static_cast<uint>(argc - 1); i < ei; ++i)
+
+ int nArgs = (argc - 1 >= 0) ? argc - 1 : 0;
+ if (target->isBoundFunction()) {
+ BoundFunction *bound = static_cast<BoundFunction *>(target.getPointer());
+ Scoped<MemberData> oldArgs(scope, bound->boundArgs());
+ boundThis = bound->boundThis();
+ int oldSize = oldArgs->size();
+ boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs);
+ boundArgs->d()->values.size = oldSize + nArgs;
+ for (uint i = 0; i < static_cast<uint>(oldSize); ++i)
+ boundArgs->set(scope.engine, i, oldArgs->data()[i]);
+ for (uint i = 0; i < static_cast<uint>(nArgs); ++i)
+ boundArgs->set(scope.engine, oldSize + i, argv[i + 1]);
+ target = bound->target();
+ } else if (nArgs) {
+ boundArgs = MemberData::allocate(scope.engine, nArgs);
+ boundArgs->d()->values.size = nArgs;
+ for (uint i = 0, ei = static_cast<uint>(nArgs); i < ei; ++i)
boundArgs->set(scope.engine, i, argv[i + 1]);
}
- ExecutionContext *global = scope.engine->rootContext();
- return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue();
+ ScopedContext ctx(scope, target->scope());
+ Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs);
+ bound->setFunction(target->function());
+ return bound->asReturnedValue();
}
DEFINE_OBJECT_VTABLE(ScriptFunction);
@@ -393,8 +411,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
FunctionObject::init();
this->scope.set(scope->engine(), scope->d());
- this->function = function;
- function->compilationUnit->addref();
+ setFunction(function);
Q_ASSERT(function);
Q_ASSERT(function->code);
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index d6066ec648..32e71a175b 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -90,6 +90,8 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) {
void init();
void destroy();
+ void setFunction(Function *f);
+
unsigned int formalParameterCount() { return function ? function->nFormals : 0; }
unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; }
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index c1bbe2a330..816c259b9b 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -460,9 +460,12 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
+ QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
QV4::ScopedContext ctx(scope, bindingFunction->scope());
- newBinding = QQmlBinding::create(property, bindingFunction->function(), object, callingQmlContext, ctx);
+ newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
+ if (f->isBoundFunction())
+ newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
newBinding->setTarget(object, *property, nullptr);
}
}
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 2506777e76..0211ad1011 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1271,7 +1271,9 @@ QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine
ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
{
- return engine->qmlContext()->asReturnedValue();
+ Heap::QmlContext *ctx = engine->qmlContext();
+ Q_ASSERT(ctx);
+ return ctx->asReturnedValue();
}
ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index c314833304..30a18440a8 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -51,6 +51,7 @@
#include <private/qqmlvaluetypewrapper_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4variantobject_p.h>
+#include <private/qv4jscall_p.h>
#include <QVariant>
#include <QtCore/qdebug.h>
@@ -97,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
return b;
}
+QQmlSourceLocation QQmlBinding::sourceLocation() const
+{
+ if (m_sourceLocation)
+ return *m_sourceLocation;
+ return QQmlJavaScriptExpression::sourceLocation();
+}
+
+void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
+{
+ if (m_sourceLocation)
+ delete m_sourceLocation;
+ m_sourceLocation = new QQmlSourceLocation(location);
+}
+
+
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
QQmlContextData *ctxt, const QString &url, quint16 lineNumber)
{
@@ -128,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function
QQmlBinding::~QQmlBinding()
{
+ delete m_sourceLocation;
}
void QQmlBinding::setNotifyOnValueChanged(bool v)
@@ -171,6 +188,28 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
setUpdatingFlag(false);
}
+QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
+{
+ QV4::ExecutionEngine *v4 = context()->engine->handle();
+ int argc = 0;
+ const QV4::Value *argv = nullptr;
+ const QV4::Value *thisObject = nullptr;
+ QV4::BoundFunction *b = nullptr;
+ if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
+ QV4::Heap::MemberData *args = b->boundArgs();
+ if (args) {
+ argc = args->values.size;
+ argv = args->values.data();
+ }
+ thisObject = &b->d()->boundThis;
+ }
+ QV4::Scope scope(v4);
+ QV4::JSCallData jsCall(scope, argc, argv, thisObject);
+
+ return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined);
+}
+
+
// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or
// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant
// expression for the switch for the compiler to generate the optimal code, but
@@ -203,7 +242,7 @@ protected:
bool isUndefined = false;
- QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
+ QV4::ScopedValue result(scope, evaluate(&isUndefined));
bool error = false;
if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
@@ -302,9 +341,14 @@ public:
{
setCompilationUnit(compilationUnit);
m_binding = binding;
- setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column));
}
+ QQmlSourceLocation sourceLocation() const override final
+ {
+ return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
+ }
+
+
void doUpdate(const DeleteWatcher &watcher,
QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
{
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index b67b02975f..a1295bd0ac 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -104,6 +104,12 @@ public:
QString expressionIdentifier() const override;
void expressionChanged() override;
+ QQmlSourceLocation sourceLocation() const override;
+ void setSourceLocation(const QQmlSourceLocation &location);
+ void setBoundFunction(QV4::BoundFunction *boundFunction) {
+ m_boundFunction.set(boundFunction->engine(), *boundFunction);
+ }
+
/**
* This method returns a snapshot of the currently tracked dependencies of
* this binding. The dependencies can change upon reevaluation. This method is
@@ -123,6 +129,8 @@ protected:
bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags);
+ QV4::ReturnedValue evaluate(bool *isUndefined);
+
private:
inline bool updatingFlag() const;
inline void setUpdatingFlag(bool);
@@ -130,6 +138,9 @@ private:
inline void setEnabledFlag(bool);
static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property);
+
+ QQmlSourceLocation *m_sourceLocation = nullptr; // used for Qt.binding() created functions
+ QV4::PersistentValue m_boundFunction; // used for Qt.binding() that are created from a bound function object
};
bool QQmlBinding::updatingFlag() const
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 40cf1417d0..93ec9421ed 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -97,8 +97,7 @@ QQmlJavaScriptExpression::QQmlJavaScriptExpression()
m_context(nullptr),
m_prevExpression(nullptr),
m_nextExpression(nullptr),
- m_v4Function(nullptr),
- m_sourceLocation(nullptr)
+ m_v4Function(nullptr)
{
}
@@ -115,8 +114,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
clearError();
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
m_scopeObject.asT2()->_s = nullptr;
-
- delete m_sourceLocation;
}
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
@@ -137,20 +134,11 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const
{
- if (m_sourceLocation)
- return *m_sourceLocation;
if (m_v4Function)
return m_v4Function->sourceLocation();
return QQmlSourceLocation();
}
-void QQmlJavaScriptExpression::setSourceLocation(const QQmlSourceLocation &location)
-{
- if (m_sourceLocation)
- delete m_sourceLocation;
- m_sourceLocation = new QQmlSourceLocation(location);
-}
-
void QQmlJavaScriptExpression::setContext(QQmlContextData *context)
{
if (m_prevExpression) {
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index bff8866011..01af3b89ca 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -116,8 +116,7 @@ public:
inline QObject *scopeObject() const;
inline void setScopeObject(QObject *v);
- QQmlSourceLocation sourceLocation() const;
- void setSourceLocation(const QQmlSourceLocation &location);
+ virtual QQmlSourceLocation sourceLocation() const;
bool isValid() const { return context() != nullptr; }
@@ -188,7 +187,6 @@ private:
QV4::PersistentValue m_qmlScope;
QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
QV4::Function *m_v4Function;
- QQmlSourceLocation *m_sourceLocation; // used for Qt.binding() created functions
};
class QQmlPropertyCapture
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index a28115d192..6196a09d94 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -463,8 +463,12 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
- QV4::ScopedContext ctx(scope, bindingFunction->scope());
- QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx);
+ QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
+ QV4::ScopedContext ctx(scope, f->scope());
+ QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx);
+ newBinding->setSourceLocation(bindingFunction->currentLocation());
+ if (f->isBoundFunction())
+ newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
newBinding->setSourceLocation(bindingFunction->currentLocation());
newBinding->setTarget(referenceObject, cacheData, pd);
QQmlPropertyPrivate::setBinding(newBinding);
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index 1371f1f041..9e7c84011b 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -1344,11 +1344,12 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co
return QQmlLocale::locale(scope.engine, code);
}
-void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction)
+void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction)
{
- Scope scope(originalFunction->engine());
- ScopedContext context(scope, originalFunction->scope());
- FunctionObject::init(context, originalFunction->function());
+ Scope scope(bindingFunction->engine());
+ ScopedContext context(scope, bindingFunction->scope());
+ FunctionObject::init(context, bindingFunction->function());
+ this->bindingFunction.set(internalClass->engine, bindingFunction->d());
}
QQmlSourceLocation QQmlBindingFunction::currentLocation() const
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index 104dae5d79..ee3b5f7d6e 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -80,8 +80,11 @@ struct ConsoleObject : Object {
void init();
};
-struct QQmlBindingFunction : FunctionObject {
- void init(const QV4::FunctionObject *originalFunction);
+#define QQmlBindingFunctionMembers(class, Member) \
+ Member(class, Pointer, FunctionObject *, bindingFunction)
+DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) {
+ DECLARE_MARKOBJECTS(QQmlBindingFunction)
+ void init(const QV4::FunctionObject *bindingFunction);
};
}
@@ -179,6 +182,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject
{
V4_OBJECT2(QQmlBindingFunction, FunctionObject)
+ Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; }
QQmlSourceLocation currentLocation() const; // from caller stack trace
};
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index e62e4a0980..f862cdb048 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -129,6 +129,7 @@ private slots:
void arraySort();
void lookupOnDisappearingProperty();
void arrayConcat();
+ void recursiveBoundFunctions();
void qRegExpInport_data();
void qRegExpInport();
@@ -3022,6 +3023,18 @@ void tst_QJSEngine::arrayConcat()
QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12"));
}
+void tst_QJSEngine::recursiveBoundFunctions()
+{
+
+ QJSEngine eng;
+ QJSValue v = eng.evaluate("function foo(x, y, z)"
+ "{ return this + x + y + z; }"
+ "var bar = foo.bind(-1, 10);"
+ "var baz = bar.bind(-2, 20);"
+ "baz(30)");
+ QCOMPARE(v.toInt(), 59);
+}
+
static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
void tst_QJSEngine::qRegExpInport_data()
diff --git a/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml b/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml
new file mode 100644
index 0000000000..8dbd2fd3d9
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml
@@ -0,0 +1,34 @@
+import QtQuick 2.6
+
+QtObject {
+ property bool success: false
+ property var num: 100
+ property var simple: 0
+ property var complex: 0
+
+
+ Component.onCompleted: {
+ function s(x) {
+ return x
+ }
+ function c(x) {
+ return x + num
+ }
+
+ var bound = s.bind(undefined, 100)
+ simple = Qt.binding(bound)
+ if (simple != 100)
+ return;
+ var bound = c.bind(undefined, 100)
+ complex = Qt.binding(bound);
+
+ if (complex != 200)
+ return;
+ num = 0;
+ if (complex != 100)
+ return;
+
+ print("success!!!");
+ success = true;
+ }
+}
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index c0cf123243..f40a9758f7 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -289,6 +289,7 @@ private slots:
void withStatement();
void tryStatement();
void replaceBinding();
+ void bindingBoundFunctions();
void deleteRootObjectInCreation();
void onDestruction();
void onDestructionViaGC();
@@ -7247,6 +7248,17 @@ void tst_qqmlecmascript::replaceBinding()
delete obj;
}
+void tst_qqmlecmascript::bindingBoundFunctions()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("bindingBoundFunctions.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj != nullptr);
+
+ QVERIFY(obj->property("success").toBool());
+ delete obj;
+}
+
void tst_qqmlecmascript::deleteRootObjectInCreation()
{
{