[Kde-bindings] Another segfault yet, but more complex
Richard Dale
Richard_Dale at tipitina.demon.co.uk
Tue Oct 5 20:32:27 UTC 2004
On Tuesday 05 October 2004 19:13, Han Holl wrote:
> Hello Richard,
>
> I'vr got another segfault for you, but I haven't succeeded in creating a
> _very_ simple script to demonstrate.
>
> I've investigated somewhat myself, and added debugging information around
> the call to checkarg, like this:
>
> (0...args.length).each do |i|
> tmp = checkarg( getVALUEtype(args[i]), getTypeNameOfArg(id, i) )
> puts "ISCORE: #{i} #{tmp} #{args[i]} #{getVALUEtype(args[i])}
> #{getTypeNameOfArg(id, i)}" if debug_level >= DebugLevel::High
> current_match += tmp
> end
>
> (Sorry for the lousy formatting). Anyway, what happens is that at some
> point I get this output:
> ISCORE: 0 -99 #<Qt::Widget:0x43230f68> QObject QWidget*
>
> which means that even though the object args[i] is Qt::Widget:0x43230f68,
> getVALUEtype(args[i]) returns QObject (instead of QWidget).
> What happens then that checharg return -99, and a segfault.
>
> I've prepared an archive which hopefully runs standalone. Because it turns
> out to be only 7kb I've attached it directly to this post.
>
> I hope you can find out what's happening.
Well confusing..
I think I know what the problem is. In handlers.cpp there is a function called
resolve_classname(), which uses various Qt RTTI mechanims to get a
better/more accurate class name to instantiate as a ruby instance.
For instance, if there is a method returning say a 'QObject*' in the api and
it returns an instance that was constructed in the C++ world (or it might be
a ruby instance which went out of scope and got garbage collected, but the
C++ instance wasn't deleted). Then it is possible to use QObject::className()
to find out what the real class name of the QObject instance is. If you add
this debugging to the code just before the show page:
puts "parent class: #{self.parent} #{self.parent.className}"
It prints this:
parent class: #<Qt::Widget:0x30466e38> Qt::Widget
So both ruby and Qt are agreed that it's a Qt::Widget, only Smoke thinks it's
a QObject. If it was a 'QWidget' rather than a 'Qt::Widget' that would mean
it had been allocated in the C++ world. So here the ruby instance was garbage
collected, but not the underlying C++ instance. Subsequently the C++ instance
was rewrapped as a Qt::Widget.
Associated with each instance is a struct, which holds a 'void *' pointer to
the underlying C++ instance, along with a numerice identifer, the 'int
classId' which is an index into an array of class names:
struct smokeruby_object {
bool allocated;
Smoke *smoke;
int classId;
void *ptr;
};
It's this classId int which is wrong in this case. Here's the relevant code in
handlers.cpp:
smokeruby_object * o = (smokeruby_object *)
malloc(sizeof(smokeruby_object));
o->smoke = m->smoke();
o->classId = m->type().classId();
o->ptr = p;
o->allocated = false;
const char * classname = resolve_classname(m, o->ptr);
...
obj = set_obj_info(classname, o);
It leaves the o->classId value as it was (representing a Smoke QObject in this
case), even though resolve_classname() has improved on it. set_obj_info()
then uses that classname to construct the ruby instance, like this:
VALUE
set_obj_info(const char * className, smokeruby_object * o)
{
VALUE klass = rb_funcall(qt_internal_module,
rb_intern("find_class"),
1,
rb_str_new2(className) );
VALUE obj = Data_Wrap_Struct(klass, smokeruby_mark, smokeruby_free, (void
*) o);
return obj;
}
This improved method arg type matching really has thrown up a whole pile of
interesting bugs. I fixed a couple of things with the rbuic tool today too as
a result..
I would make widget an instance variable @widget in the meantime, so ruby
doesn't garbage collect it, along with anything else long lived. You can
debug garbage collection like this:
Qt::Internal::setDebug(Qt::QtDebugChannel::QTDB_GC)
-- Richard
More information about the Kde-bindings
mailing list