[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