diff options
author | Kent Hansen <kent.hansen@nokia.com> | 2012-07-13 13:15:09 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-07-13 19:47:34 +0200 |
commit | d08de20171c0df99513514af050372fc1aee8720 (patch) | |
tree | c6a9e62706fac3f40539d2f183d3d5b8e105fc14 | |
parent | db17c14cace450e20745839014075c0263f8618f (diff) | |
download | qtscript-d08de20171c0df99513514af050372fc1aee8720.tar.gz |
Fix QScriptContextInfo::functionMetaIndex() for overloaded slots
This was a regression against the pre-JavaScriptCore
implementation, where we used to store the selected method index as
an internal member of the QScriptContext(Private). But in the
JSC-based implementation, QScriptContext is implemented as a
pointer to a JSC::CallFrame, and there are no unused fields in
CallFrame where the method index can be stashed.
Refactor the Qt method call logic so that the method selection is
separate from the actual processing of the target method. This way,
QScriptContextInfo can compute the method index the same way that
the actual method call did.
Task-number: QTBUG-6133
Change-Id: I619fa8b91542d0b6ab5a44b00266cc0705c95823
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
-rw-r--r-- | src/script/api/qscriptcontextinfo.cpp | 3 | ||||
-rw-r--r-- | src/script/bridge/qscriptqobject.cpp | 186 | ||||
-rw-r--r-- | src/script/bridge/qscriptqobject_p.h | 1 | ||||
-rw-r--r-- | tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp | 6 |
4 files changed, 130 insertions, 66 deletions
diff --git a/src/script/api/qscriptcontextinfo.cpp b/src/script/api/qscriptcontextinfo.cpp index 31e7cfc..0e7884f 100644 --- a/src/script/api/qscriptcontextinfo.cpp +++ b/src/script/api/qscriptcontextinfo.cpp @@ -197,8 +197,7 @@ QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *conte // ### get the function name from the AST } else if (callee && callee->inherits(&QScript::QtFunction::info)) { functionType = QScriptContextInfo::QtFunction; - // ### the slot can be overloaded -- need to get the particular overload from the context - functionMetaIndex = static_cast<QScript::QtFunction*>(callee)->initialIndex(); + functionMetaIndex = static_cast<QScript::QtFunction*>(callee)->specificIndex(context); const QMetaObject *meta = static_cast<QScript::QtFunction*>(callee)->metaObject(); if (meta != 0) { QMetaMethod method = meta->method(functionMetaIndex); diff --git a/src/script/bridge/qscriptqobject.cpp b/src/script/bridge/qscriptqobject.cpp index 15288a7..3e82dbb 100644 --- a/src/script/bridge/qscriptqobject.cpp +++ b/src/script/bridge/qscriptqobject.cpp @@ -441,10 +441,15 @@ static QMetaMethod metaMethod(const QMetaObject *meta, return meta->constructor(index); } -static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType, - QObject *thisQObject, const JSC::ArgList &scriptArgs, - const QMetaObject *meta, int initialIndex, - bool maybeOverloaded) +/*! + \internal + Derives the actual method to call based on the script arguments, + \a scriptArgs, and delegates it to the given \a delegate. +*/ +template <class Delegate> +static JSC::JSValue delegateQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType, + const JSC::ArgList &scriptArgs, const QMetaObject *meta, + int initialIndex, bool maybeOverloaded, Delegate &delegate) { QScriptMetaMethod chosenMethod; int chosenIndex = -1; @@ -456,7 +461,6 @@ static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType c int index; QByteArray methodName; exec->clearException(); - QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec); for (index = initialIndex; index >= 0; --index) { QMetaMethod method = metaMethod(meta, callType, index); @@ -844,70 +848,97 @@ static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType c } } - if (chosenIndex != -1) { - // call it -// context->calleeMetaIndex = chosenIndex; - - QVarLengthArray<void*, 9> array(args.count()); - void **params = array.data(); - for (int i = 0; i < args.count(); ++i) { - const QVariant &v = args[i]; - switch (chosenMethod.type(i).kind()) { - case QScriptMetaType::Variant: - params[i] = const_cast<QVariant*>(&v); - break; - case QScriptMetaType::MetaType: - case QScriptMetaType::MetaEnum: - case QScriptMetaType::Unresolved: - params[i] = const_cast<void*>(v.constData()); - break; - default: - Q_ASSERT(0); - } - } + if (chosenIndex != -1) + result = delegate(exec, callType, meta, chosenMethod, chosenIndex, args); + } - QScriptable *scriptable = 0; - if (thisQObject) - scriptable = scriptableFromQObject(thisQObject); - QScriptEngine *oldEngine = 0; - if (scriptable) { - oldEngine = QScriptablePrivate::get(scriptable)->engine; - QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); + return result; +} + +struct QtMethodCaller +{ + QtMethodCaller(QObject *o) + : thisQObject(o) + {} + JSC::JSValue operator()(JSC::ExecState *exec, QMetaMethod::MethodType callType, + const QMetaObject *meta, const QScriptMetaMethod &chosenMethod, + int chosenIndex, const QVarLengthArray<QVariant, 9> &args) + { + JSC::JSValue result; + + QVarLengthArray<void*, 9> array(args.count()); + void **params = array.data(); + for (int i = 0; i < args.count(); ++i) { + const QVariant &v = args[i]; + switch (chosenMethod.type(i).kind()) { + case QScriptMetaType::Variant: + params[i] = const_cast<QVariant*>(&v); + break; + case QScriptMetaType::MetaType: + case QScriptMetaType::MetaEnum: + case QScriptMetaType::Unresolved: + params[i] = const_cast<void*>(v.constData()); + break; + default: + Q_ASSERT(0); } + } -// ### fixme -//#ifndef Q_SCRIPT_NO_EVENT_NOTIFY -// engine->notifyFunctionEntry(context); -//#endif + QScriptable *scriptable = 0; + if (thisQObject) + scriptable = scriptableFromQObject(thisQObject); + QScriptEngine *oldEngine = 0; + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec); + if (scriptable) { + oldEngine = QScriptablePrivate::get(scriptable)->engine; + QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); + } - if (callType == QMetaMethod::Constructor) { - Q_ASSERT(meta != 0); - meta->static_metacall(QMetaObject::CreateInstance, chosenIndex, params); - } else { - QMetaObject::metacall(thisQObject, QMetaObject::InvokeMetaMethod, chosenIndex, params); - } + // ### fixme + //#ifndef Q_SCRIPT_NO_EVENT_NOTIFY + // engine->notifyFunctionEntry(context); + //#endif - if (scriptable) - QScriptablePrivate::get(scriptable)->engine = oldEngine; + if (callType == QMetaMethod::Constructor) { + Q_ASSERT(meta != 0); + meta->static_metacall(QMetaObject::CreateInstance, chosenIndex, params); + } else { + QMetaObject::metacall(thisQObject, QMetaObject::InvokeMetaMethod, chosenIndex, params); + } - if (exec->hadException()) { - result = exec->exception() ; // propagate + if (scriptable) + QScriptablePrivate::get(scriptable)->engine = oldEngine; + + if (exec->hadException()) { + result = exec->exception() ; // propagate + } else { + QScriptMetaType retType = chosenMethod.returnType(); + if (retType.isVariant()) { + result = QScriptEnginePrivate::jscValueFromVariant(exec, *(QVariant *)params[0]); + } else if (retType.typeId() != QMetaType::Void) { + result = QScriptEnginePrivate::create(exec, retType.typeId(), params[0]); + if (!result) + result = engine->newVariant(QVariant(retType.typeId(), params[0])); } else { - QScriptMetaType retType = chosenMethod.returnType(); - if (retType.isVariant()) { - result = QScriptEnginePrivate::jscValueFromVariant(exec, *(QVariant *)params[0]); - } else if (retType.typeId() != QMetaType::Void) { - result = QScriptEnginePrivate::create(exec, retType.typeId(), params[0]); - if (!result) - result = engine->newVariant(QVariant(retType.typeId(), params[0])); - } else { - result = JSC::jsUndefined(); - } + result = JSC::jsUndefined(); } } + + return result; } - return result; +private: + QObject *thisQObject; +}; + +static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType, + QObject *thisQObject, const JSC::ArgList &scriptArgs, + const QMetaObject *meta, int initialIndex, + bool maybeOverloaded) +{ + QtMethodCaller caller(thisQObject); + return delegateQtMethod<QtMethodCaller>(exec, callType, scriptArgs, meta, + initialIndex, maybeOverloaded, caller); } JSC::JSValue QtFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue, @@ -960,6 +991,45 @@ JSC::JSValue JSC_HOST_CALL QtFunction::call(JSC::ExecState *exec, JSC::JSObject return result; } +struct QtMethodIndexReturner +{ + JSC::JSValue operator()(JSC::ExecState *exec, QMetaMethod::MethodType, + const QMetaObject *, const QScriptMetaMethod &, + int chosenIndex, const QVarLengthArray<QVariant, 9> &) + { + return JSC::jsNumber(exec, chosenIndex); + } +}; + +/*! + \internal + Returns the specific index of the meta-method that was used in the + function call represented by the given \a context. If the method is + overloaded, the actual parameters that were passed to the function + are used to derive the selected index, matching the behavior of + callQtMethod(). +*/ +int QtFunction::specificIndex(const QScriptContext *context) const +{ + if (!maybeOverloaded()) + return initialIndex(); + JSC::ExecState *exec = const_cast<JSC::ExecState *>(QScriptEnginePrivate::frameForContext(context)); + int argCount = exec->argumentCount(); + + // Create arguments list wrapper; this logic must match + // JITStubs.cpp op_call_NotJSFunction, and Interpreter.cpp op_call + JSC::Register* argv = exec->registers() - JSC::RegisterFile::CallFrameHeaderSize - argCount; + JSC::ArgList args(argv + 1, argCount - 1); + + QtMethodIndexReturner returner; + JSC::JSValue result = delegateQtMethod<QtMethodIndexReturner>( + exec, QMetaMethod::Method, args, metaObject(), + initialIndex(), maybeOverloaded(), returner); + if (exec->hadException() || !result || !result.isInt32()) + return initialIndex(); + return result.asInt32(); +} + const JSC::ClassInfo QtPropertyFunction::info = { "QtPropertyFunction", &InternalFunction::info, 0, 0 }; QtPropertyFunction::QtPropertyFunction(const QMetaObject *meta, int index, diff --git a/src/script/bridge/qscriptqobject_p.h b/src/script/bridge/qscriptqobject_p.h index c6ebc8a..55ed0f7 100644 --- a/src/script/bridge/qscriptqobject_p.h +++ b/src/script/bridge/qscriptqobject_p.h @@ -225,6 +225,7 @@ public: QObject *qobject() const; const QMetaObject *metaObject() const; int initialIndex() const; + int specificIndex(const QScriptContext *context) const; bool maybeOverloaded() const; int mostGeneralMethod(QMetaMethod *out = 0) const; QList<int> overloadedIndexes() const; diff --git a/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp b/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp index 1dea062..7816e52 100644 --- a/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp +++ b/tests/auto/qscriptcontextinfo/tst_qscriptcontextinfo.cpp @@ -246,14 +246,8 @@ void tst_QScriptContextInfo::qtFunction() QCOMPARE(info.functionName(), QString::fromLatin1("testSlot")); QCOMPARE(info.functionEndLineNumber(), -1); QCOMPARE(info.functionStartLineNumber(), -1); - if (x == 0) - QEXPECT_FAIL("", "QTBUG-6133: QScriptContextInfo doesn't pick the correct meta-index for overloaded slots", Continue); QCOMPARE(info.functionParameterNames().size(), pnames.size()); - if (x == 0) - QEXPECT_FAIL("", "QTBUG-6133: QScriptContextInfo doesn't pick the correct meta-index for overloaded slots", Continue); QCOMPARE(info.functionParameterNames(), pnames); - if (x == 0) - QEXPECT_FAIL("", "QTBUG-6133: QScriptContextInfo doesn't pick the correct meta-index for overloaded slots", Continue); QCOMPARE(info.functionMetaIndex(), metaObject()->indexOfMethod(sig)); } |