[Qt-interest] QtScript: qscriptvalue_cast and prototype chains
Alexander stillich
torque at themetroplex.org
Fri Sep 16 17:47:59 CEST 2011
Hi List,
I export non-QObject classes to QtScript using setDefaultPrototype() and
registering constructor functions, like this:
Q_DECLARE_METATYPE(Base)
Q_DECLARE_METATYPE(Base *)
static QScriptValue registerBase(QScriptEngine *engine)
{
QScriptValue proto = engine->newObject();
QScriptValue ctor = engine->newFunction(Base_ctor, proto);
engine->setDefaultPrototype(qMetaTypeId<Base>(), proto);
engine->setDefaultPrototype(qMetaTypeId<Base *>(), proto);
engine->globalObject().setProperty("Base", ctor);
return proto;
}
Everything worked out until I tried to export classes of a hierarchy and
casting parameters passed to functions to the base class. I'm trying to
cast
the QScriptValue parameter passed to the function to the base class using
qscriptvalue_cast<Base*>(), which fails when passing a derived class:
static QScriptValue foobar(QScriptContext *context, QScriptEngine
*engine)
{
QScriptValue base = engine->globalObject().property("Base");
// prints true, prototype chain seems OK
qDebug() << "instanceof" << context->argument(0).instanceOf(base);
// PROBLEM: is 0, doesn't seem to resolve the prototype chain
with qscriptvalue_cast
qDebug() << "argument as Base is:" <<
qscriptvalue_cast<Base*>(context->argument(0));
// succeeds, 1:1 match
qDebug() << "argument as Derived is:" <<
qscriptvalue_cast<Derived*>(context->argument(0));
return engine->undefinedValue();
}
This is the test script:
foobar(new Derived());
Knowing JavaScript prototype chains I had guessed that when I set up one
for the two types,
QS would know the relationship between the types and do the cast:
static QScriptValue registerDerived(QScriptValue base_proto,
QScriptEngine *engine)
{
QScriptValue proto = engine->newObject();
proto.setPrototype(base_proto);
QScriptValue ctor = engine->newFunction(Derived_ctor);
engine->setDefaultPrototype(qMetaTypeId<Derived>(), proto);
engine->setDefaultPrototype(qMetaTypeId<Derived *>(), proto);
engine->globalObject().setProperty("Derived", ctor);
return proto;
}
The answer by Kent Hansen in this thread
http://lists.trolltech.com/qt-interest/2007-06/thread00842-0.html
suggests exactly this. This is not the case, however. I have stepped
through
QScriptEngine::convertValue (which is used by qscriptvalue_cast) and it
does
indeed try to look at the prototype chain, however the prototypes are never
actually tested:
In qscriptengine.cpp:3240 (Qt 4.7.4):
while (proto.isObject()) {
bool canCast = false;
if (isVariant(proto)) {
canCast = (type == variantValue(proto).userType())
|| (valueType && (valueType ==
variantValue(proto).userType()));
}
#ifndef QT_NO_QOBJECT
else if (isQObject(proto)) {
QByteArray className = name.left(name.size()-1);
if (QObject *qobject = toQObject(exec, proto))
canCast = qobject->qt_metacast(className) != 0;
}
#endif
// ...
}
The code tests if the prototype is a variant or a QObject, which it is both
not. This doesn't really make sense to me, as the prototype is a
QScriptValue
created with engine->newObject(), which doesn't wrap anything in a QVariant
(I think).
So, the cast fails, because QS thinks the types are unrelated. Using
QScriptValue::instanceOf() on the argument and passing the base class JS
constructor gives true. But instanceOf() seems to use JavaScriptCore to
figure that out (in qscriptvalue.cpp:619).
This leads me to believe that Qt's prototype resolving is wrong in this
case? Or maybe I'm doing something wrong. Any ideas?
Attached is a sample project demonstrating the problem.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: testscript.zip
Type: application/x-zip-compressed
Size: 1221 bytes
Desc: not available
Url : http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20110916/cb95689a/attachment.bin
More information about the Qt-interest-old
mailing list