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@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& 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& 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& includes) +QString SmokeClassFiles::generateMethodBody(const QString& indent, const QString& className, const QString& smokeClassName, const Method& meth, + int index, bool dynamicDispatch, QSet& 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& 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()) { _______________________________________________ Kde-bindings mailing list Kde-bindings@kde.org https://mail.kde.org/mailman/listinfo/kde-bindings