[Kde-bindings] KDE/kdebindings/generator/generators/smoke

Arno Rehn kde at arnorehn.de
Thu Jun 10 23:33:27 UTC 2010


SVN commit 1136866 by arnorehn:

Fix this long-standing bug in SMOKE:

Previously, a method call through SMOKE would always call a specific implementation, even though dynamic dispatch
would be the correct calling way. The problem was to distinguish between cases where dynamic dispatch is the correct
way of calling and cases where that specific method and nothing else should be called.

This is now fixed in the following way: if an object is created through smoke (i.e. it's really an x_Foo class), then we
DO NOT use dynamic dispatch, so calls to the parent implemenation of a method will work.

However, if the object is not an x_Foo class (i.e. it was returned by some other method), then we use dynamic dispatch, so
internal classes that are not wrapped by the bindings will still work (e.g. subclasses of KPluginFactory, KParts::Part, etc...).

A virtual method call now looks like this:

        // virtual const QMetaObject* metaObject() const
        if (typeid(*this) == typeid(x_QObject)) {
            const QMetaObject* xret = ((const x_QObject*)this)->QObject::metaObject();
            x[0].s_class = (void*)xret;
        } else {
            const QMetaObject* xret = ((const x_QObject*)this)->metaObject();
            x[0].s_class = (void*)xret;
        }

The overhead for the typeid() calls should be minimal. The information is either provided at compile time or directly retrieved from the vtable.

CCMAIL: kde-bindings at kde.org

 M  +1 -0      globals.h  
 M  +42 -14    writeClasses.cpp  


--- trunk/KDE/kdebindings/generator/generators/smoke/globals.h #1136865:1136866
@@ -86,6 +86,7 @@
     void write(const QList<QString>& keys);
 
 private:
+    QString generateMethodBody(const QString& indent, const QString& className, const QString& smokeClassName, const Method& meth, int index, bool dynamicDispatch, QSet< QString >& includes);
     void generateMethod(QTextStream& out, const QString& className, const QString& smokeClassName, const Method& meth, int index, QSet<QString>& includes);
     void generateGetAccessor(QTextStream& out, const QString& className, const Field& field, const Type* type, int index);
     void generateSetAccessor(QTextStream& out, const QString& className, const Field& field, const Type* type, int index);
--- trunk/KDE/kdebindings/generator/generators/smoke/writeClasses.cpp #1136865:1136866
@@ -91,16 +91,14 @@
     }
 }
 
-void SmokeClassFiles::generateMethod(QTextStream& out, const QString& className, const QString& smokeClassName,
-                                     const Method& meth, int index, QSet<QString>& includes)
+QString SmokeClassFiles::generateMethodBody(const QString& indent, const QString& className, const QString& smokeClassName, const Method& meth,
+                                            int index, bool dynamicDispatch, QSet<QString>& includes)
 {
-    out << "    ";
-    if ((meth.flags() & Method::Static) || meth.isConstructor())
-        out << "static ";
-    out << QString("void x_%1(Smoke::Stack x) {\n").arg(index);
-    out << "        // " << meth.toString() << "\n";
-    out << "        ";
+    QString methodBody;
+    QTextStream out(&methodBody);
     
+    out << indent;
+
     if (meth.isConstructor()) {
         out << smokeClassName << "* xret = new " << smokeClassName << "(";
     } else {
@@ -123,8 +121,8 @@
                 out << "this->";
             }
         }
-        if (!(meth.flags() & Method::PureVirtual) && !(meth.flags() & Method::DynamicDispatch) && !func) {
-            // dynamic dispatch for virtuals
+        if (!dynamicDispatch && !func) {
+            // dynamic dispatch not wanted, call with 'this->Foo::method()'
             out << className << "::";
         } else if (func) {
             if (!func->nameSpace().isEmpty())
@@ -159,8 +157,7 @@
         out << "(" << typeName << ")" << "x[" << j + 1 << "]." << field;
     }
     
-    // if the method has any other default parameters, append them here as values, so 
-    
+    // if the method has any other default parameters, append them here as values
     if (!meth.remainingDefaultValues().isEmpty()) {
         const QStringList& defaultParams = meth.remainingDefaultValues();
         if (meth.parameters().count() > 0)
@@ -170,13 +167,44 @@
     
     out << ");\n";
     if (meth.type() != Type::Void) {
-        out << "        x[0]." << Util::stackItemField(meth.type()) << " = " << Util::assignmentString(meth.type(), "xret") << ";\n";
+        out << indent << "x[0]." << Util::stackItemField(meth.type()) << " = " << Util::assignmentString(meth.type(), "xret") << ";\n";
     } else {
-        out << "        (void)x; // noop (for compiler warning)\n";
+        out << indent << "(void)x; // noop (for compiler warning)\n";
     }
     
+    return methodBody;
+}
+
+void SmokeClassFiles::generateMethod(QTextStream& out, const QString& className, const QString& smokeClassName,
+                                     const Method& meth, int index, QSet<QString>& includes)
+{
+    out << "    ";
+    if ((meth.flags() & Method::Static) || meth.isConstructor())
+        out << "static ";
+    out << QString("void x_%1(Smoke::Stack x) {\n").arg(index);
+    out << "        // " << meth.toString() << "\n";
+
+    bool dynamicDispatch = ((meth.flags() & Method::PureVirtual) || (meth.flags() & Method::DynamicDispatch));
+
+    if (dynamicDispatch || !Util::virtualMethodsForClass(meth.getClass()).contains(&meth)) {
+        // This is either already flagged as dynamic dispatch or just a normal method. We can generate a normal method call for it.
+
+        out << generateMethodBody("        ",   // indent
+                                  className, smokeClassName, meth, index, dynamicDispatch, includes);
+    } else {
+        // This is a virtual method. To know whether we should call with dynamic dispatch, we need a bit of RTTI magic.
+        includes.insert("typeinfo");
+        out << "        if (typeid(*this) == typeid(" << smokeClassName << ")) {\n";   //
+        out << generateMethodBody("            ",   // indent
+                                  className, smokeClassName, meth, index, false, includes);
+        out << "        } else {\n";
+        out << generateMethodBody("            ",   // indent
+                                  className, smokeClassName, meth, index, true, includes);
     out << "    }\n";
+    }
     
+    out << "    }\n";
+    
     // If the constructor was generated from another one with default parameteres, we don't need to explicitly create
     // it here again. The x_* call will append the default parameters at the end and thus choose the right constructor.
     if (meth.isConstructor() && meth.remainingDefaultValues().isEmpty()) {



More information about the Kde-bindings mailing list