aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2019-01-02 16:09:56 +0100
committerSimon Hausmann <simon.hausmann@qt.io>2019-03-20 09:16:02 +0000
commitc9e6251cc8dfcf002f64b07e48dd68b7edd3f630 (patch)
treecf366b381c614be175d10e393b02b0c0c7779957 /src/qml/jsruntime
parentd4076f5ae93e7994e5ce5edcf35a090978613e98 (diff)
Implement dummy QML lookups for "global" variables
When resolving names in the context of QML bindings, we now direct runtime access to QQmlContextWrapper::resolveQmlPropertyLookupGetter. At the moment this does basically the same as Runtime::method_loadName, which we called earlier. However this now provides the opportunity to optimize lookups in the QML context in a central place. When performing a call on a scope or context object property, we also did not use a CallName() instruction - which would have gotten the thisObject wrong - but instead we use a dedicated CallScopeObjectProperty and CallContextObjectProperty instruction. These rely on identifying these properties at compile time, which goes away with lookups (and also doesn't work when using ahead-of-time compilation). Therefore the qml context property lookup is using a getPropertyAndBase style signature and Runtime::method_callQmlContextPropertyLookup uses that. For the tests to pass, some error expectations need adjusting. In particular the compile-time detection of write attempts to id objects is now delayed to the run-time. The old code path is still there and will be removed separately in the next commit (as it is massive). Task-number: QTBUG-69898 Change-Id: Iad1ff93d3758c4db984a7c2d003beee21ed2275c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/qv4engine.cpp8
-rw-r--r--src/qml/jsruntime/qv4engine_p.h1
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h1
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp62
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h4
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp49
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp13
8 files changed, 112 insertions, 27 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index f00578aa70..1d0c7c13fa 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -1169,6 +1169,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message)
return throwError(error);
}
+ReturnedValue ExecutionEngine::throwReferenceError(const QString &name)
+{
+ Scope scope(this);
+ QString msg = name + QLatin1String(" is not defined");
+ ScopedObject error(scope, newReferenceErrorObject(msg));
+ return throwError(error);
+}
+
ReturnedValue ExecutionEngine::throwReferenceError(const Value &value)
{
Scope scope(this);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index 64c7e2f32b..86367c0ece 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -569,6 +569,7 @@ public:
ReturnedValue throwTypeError();
ReturnedValue throwTypeError(const QString &message);
ReturnedValue throwReferenceError(const Value &value);
+ ReturnedValue throwReferenceError(const QString &name);
ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column);
ReturnedValue throwRangeError(const Value &value);
ReturnedValue throwRangeError(const QString &message);
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index b0d5706dea..e4d9ad8328 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -68,6 +68,7 @@ struct Lookup {
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
+ ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject);
bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v);
};
union {
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 88b0822f42..ae458c604a 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -55,6 +55,8 @@
#include <private/qjsvalue_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qv4module_p.h>
+#include <private/qv4lookup_p.h>
+#include <private/qv4identifiertable_p.h>
QT_BEGIN_NAMESPACE
@@ -77,14 +79,11 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base)
{
- Q_ASSERT(m->as<QQmlContextWrapper>());
-
if (!id.isString())
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
- const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -100,11 +99,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
}
}
- return Object::virtualGet(m, id, receiver, hasProperty);
+ return Object::virtualGet(resource, id, receiver, hasProperty);
}
bool hasProp = false;
- ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
+ ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -234,6 +233,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, scopeObject);
return result->asReturnedValue();
}
}
@@ -247,6 +248,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
if (hasProp) {
if (hasProperty)
*hasProperty = true;
+ if (base)
+ *base = QV4::QObjectWrapper::wrap(v4, context->contextObject);
return result->asReturnedValue();
}
}
@@ -264,6 +267,13 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c
return Encode::undefined();
}
+ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ Q_ASSERT(m->as<QQmlContextWrapper>());
+ const QQmlContextWrapper *This = static_cast<const QQmlContextWrapper *>(m);
+ return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr);
+}
+
bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
@@ -298,8 +308,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
// Search context properties
- if (properties.count() && properties.value(name) != -1)
- return false;
+ if (properties.count()) {
+ const int propertyIndex = properties.value(name);
+ if (propertyIndex != -1) {
+ if (propertyIndex < context->idValueCount) {
+ v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue"));
+ return false;
+ }
+ return false;
+ }
+ }
// Search scope object
if (scopeObject &&
@@ -323,6 +341,32 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val
return false;
}
+ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base)
+{
+ Scope scope(engine);
+ PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->
+ runtimeStrings[l->nameIndex]);
+
+ // Special hack for bounded signal expressions, where the parameters of signals are injected
+ // into the handler expression through the locals of the call context. So for onClicked: { ... }
+ // the parameters of the clicked signal are injected and we must allow for them to be found here
+ // before any other property from the QML context.
+ ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context);
+ if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) {
+ uint index = ctx.d()->internalClass->indexOfValueOrGetter(name);
+ if (index < UINT_MAX)
+ return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue();
+ }
+
+ Scoped<QQmlContextWrapper> qmlContext(scope, engine->qmlContext()->qml());
+ bool hasProperty = false;
+ ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr,
+ &hasProperty, base));
+ if (!hasProperty)
+ return engine->throwReferenceError(name.toQString());
+ return result->asReturnedValue();
+}
+
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index dd6de3323d..f704280bf4 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -99,8 +99,12 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
inline QObject *getScopeObject() const { return d()->scopeObject; }
inline QQmlContextData *getContext() const { return *d()->context; }
+ static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver,
+ bool *hasProperty, Value *base);
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
+
+ static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 53dd3a66dd..7163a51af1 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -1301,18 +1301,42 @@ uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const
return v->booleanValue();
}
+static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName)
+{
+ QString objectAsString = QStringLiteral("[null]");
+ if (!thisObject->isUndefined())
+ objectAsString = thisObject->toQStringNoThrow();
+ QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(propertyName, objectAsString);
+ return engine->throwTypeError(msg);
+}
ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc)
{
+ Scope scope(engine);
Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
Value function = Value::fromReturnedValue(l->globalGetter(l, engine));
+ Value thisObject = Value::undefinedValue();
if (!function.isFunctionObject())
- return engine->throwTypeError();
+ return throwPropertyIsNotAFunctionTypeError(engine, &thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
- Value thisObject = Value::undefinedValue();
return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
+ReturnedValue Runtime::method_callQmlContextPropertyLookup(ExecutionEngine *engine, uint index, Value *argv, int argc)
+{
+ Scope scope(engine);
+ ScopedValue thisObject(scope);
+ Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index;
+ Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject));
+ if (!function.isFunctionObject())
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
+
+ return static_cast<FunctionObject &>(function).call(thisObject, argv, argc);
+}
+
ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc)
{
Scope scope(engine);
@@ -1323,13 +1347,8 @@ ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Va
if (engine->hasException)
return Encode::undefined();
- if (!function) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!function)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval"));
if (function->d() == engine->evalFunction()->d())
return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true);
@@ -1348,15 +1367,9 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V
if (engine->hasException)
return Encode::undefined();
- if (!f) {
- QString objectAsString = QStringLiteral("[null]");
- if (!thisObject->isUndefined())
- objectAsString = thisObject->toQStringNoThrow();
- QString msg = QStringLiteral("Property '%1' of object %2 is not a function")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- objectAsString);
- return engine->throwTypeError(msg);
- }
+ if (!f)
+ return throwPropertyIsNotAFunctionTypeError(engine, thisObject,
+ engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString());
return f->call(thisObject, argv, argc);
}
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 06e8a8a960..aa4ac27621 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -93,6 +93,7 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
#define FOR_EACH_RUNTIME_METHOD(F) \
/* call */ \
F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \
+ F(ReturnedValue, callQmlContextPropertyLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \
F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \
F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \
F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 000e1ab9d5..2d9337b03e 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -556,6 +556,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadGlobalLookup)
+ MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup)
+ STORE_IP();
+ QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+ acc = l->qmlContextPropertyGetter(l, engine, nullptr);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(LoadQmlContextPropertyLookup)
+
MOTH_BEGIN_INSTR(StoreNameStrict)
STORE_IP();
STORE_ACC();
@@ -781,6 +788,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
CHECK_EXCEPTION;
MOTH_END_INSTR(CallGlobalLookup)
+ MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup)
+ STORE_IP();
+ acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallQmlContextPropertyLookup)
+
MOTH_BEGIN_INSTR(CallScopeObjectProperty)
STORE_IP();
acc = Runtime::method_callQmlScopeObjectProperty(engine, stack + base, name, stack + argv, argc);