jhass / crystal-gobject

gobject-introspection for Crystal

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bind generator doesn't use the GIR [array length=data_size] annotation

hugopl opened this issue · comments

Bind generator doesn't use the GIR [array length=data_size] annotation, e.g. the Gio function:

gchar *
g_content_type_guess (const gchar *filename,
                      const guchar *data,
                      gsize data_size,
                      gboolean *result_uncertain);```
Has the annotations:
Parameter Type Annotation
filename a string, or NULL. [nullable]
data a stream of data, or NULL. [nullable][arraylength=data_size]
data_size the size of data  
result_uncertain return location for the certainty of the result, or NULL. [out][optional]

And the generated binding is:

  def self.content_type_guess(filename : ::String?, data : ::String, data_size : ::Int, result_uncertain : Bool?)
    __return_value = LibGio.content_type_guess(filename ? filename.to_unsafe : nil, data ? data : nil, UInt64.new(data_size), result_uncertain)
    GObject.raise_unexpected_null("g_content_type_guess") if __return_value.null?
    ::String.new(__return_value)
  end

To make things more Crystal like it would be:

  def self.content_type_guess(filename : ::String?, result_uncertain : Bool?)
    __return_value = LibGio.content_type_guess(filename ? filename.to_unsafe : nil, data ? data : nil, filename ? filename.size : 0, out result_uncertain)
    GObject.raise_unexpected_null("g_content_type_guess") if __return_value.null?
    ::String.new(__return_value)
  end

BTW, this result_uncertain parameter is unusable the way it's now, but this is another issue, maybe if the parameter have the out annotation and it's a C primitive type, we could put it on return value as a Tuple with the current return value.

I reported the issue without compile my code using it... now I see that this generated function fail to compile:

Which expanded to:

 > 4942 | def self.content_type_guess(filename : ::String?, data : ::String, data_size : ::Int, result_uncertain : Bool?)
 > 4943 |   __return_value = LibGio.content_type_guess(filename ? filename.to_unsafe : nil, data ? data : nil, UInt64.new(data_size), result_uncertain)
 > 4944 |   GObject.raise_unexpected_null("g_content_type_guess") if __return_value.null?
                                                                                            ^--------
Error: undefined method 'to_unsafe' for Nil (compile-time type is (String | Nil))

Also... isn't a memory leak happening here?
Since String.new(__return_value) copies the data allocated on C side, so the original pointer need to be freed.

I'm sure there's many memory leaks still, I have been rather ignorant to the whole reference counting and ownership transfer business so far to be honest. But then with the malloc shim it's also compounded to GSlice allocated stuff I guess :D

Yes.. all crashes I had so far are related to GSlice free calls, made probably by GDK, since I have GTK, GLib and GtkSourceView compiled with symbols and there are still unamed symbols in the stacktrace... today I'll compile the other parts of GTK stack to have more useful stacktraces.

Mmh, bin/gi-dump only has this to say:

+ content_type_guess (function, Gio)
  * method = false
  * throws = false
  * skip_return = false
  * may_return_null = false
  * caller_owns = EVERYTHING
  * self_arg_ownership = NOTHING
  * return_type
      + <no name> (type, Gio)
        * tag = UTF8
        * pointer = true
  * args
      + filename (arg, Gio)
        * direction = IN
        * optional = false
        * nullable = true
        * return_value = false
        * ownership = NOTHING
        * caller_allocates = false
        * skip = false
        * type
            + <no name> (type, Gio)
              * tag = UTF8
              * pointer = true
      + data (arg, Gio)
        * direction = IN
        * optional = false
        * nullable = true
        * return_value = false
        * ownership = NOTHING
        * caller_allocates = false
        * skip = false
        * type
            + <no name> (type, Gio)
              * tag = ARRAY
              * pointer = true
              * array_type = C
              * array_length = 2
              * array_fixed_size = -1
              * param_type
                  + <no name> (type, Gio)
                    * tag = UINT8
                    * pointer = false
      + data_size (arg, Gio)
        * direction = IN
        * optional = false
        * nullable = false
        * return_value = false
        * ownership = NOTHING
        * caller_allocates = false
        * skip = false
        * type
            + <no name> (type, Gio)
              * tag = UINT64
              * pointer = false
      + result_uncertain (arg, Gio)
        * direction = OUT
        * optional = true
        * nullable = false
        * return_value = false
        * ownership = EVERYTHING
        * caller_allocates = false
        * skip = false
        * type
            + <no name> (type, Gio)
              * tag = BOOLEAN
              * pointer = false
  * flags = None
  * symbol = g_content_type_guess
  * constructor = false

So I don't quite see array length=data_size being exposed anywhere in GICallableInfo/GIFunctionInfo/GIArgInfo.

So it seems we can only make a guess based on argument name?

Still trying to dig out why it types it as String and not Bytes.

Oh, haha I always wondered why GITypeInfo would have get_array_length and get_array_fixed_size, seemed kind of redundant. So get_array_length is actually an index into the grandparent's (CallableInfo) argument list! Well this is anything but obvious. From the docs too.

Ah, and the String guess was for gtk_css_provider_load_from_data. I don't know why this is typed guchar*, anything but string data doesn't seem to make any sense. Anyways, gonna switch the guess to gchar* and make guchar* guess to Bytes, even if that means an ugly to_slice call in the user API for the above :/

EDIT: Ah, it's actually documentation bug! https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1939

Btw. any API ideas for primitive out args, like the result_uncertain above?

In my project I'm using:

module Gio
 # I removed changed String? to String just because in my project-specific case I never pass nil there, so ignore this :-)
  def self.content_type_guess(filename : ::String, data : ::String)
    __return_value = LibGio.content_type_guess(filename, data, data.size, out result_uncertain)
    GObject.raise_unexpected_null("g_content_type_guess") if __return_value.null?
    {::String.new(__return_value), result_uncertain}
  end
end

I guess this works for all cases when the out parameter is a primitive type.

If it is not a primitive type, better keep as is, e.g.:

# For TreeModel Wrapper class
    def iter_parent(iter : Gtk::TreeIter, child : Gtk::TreeIter)
      __return_value = LibGtk.tree_model_iter_parent(@pointer.as(LibGtk::TreeModel*), iter, child.to_unsafe.as(LibGtk::TreeIter*))
      __return_value
    end

iter has the out annotation, but also GTK returns a Boolean for this func, just making the signature: iter_parent(child : Gtk::TreeIter) : Tuple(Bool, Gtk::TreeIter) seems a bit weird to me.
Making it like iter_parent(child : Gtk::TreeIter) : Gtk::TreeIter? makes more sense, but I don't know if this pattern apply to all methods with a Bool return type and a single out parameters.

Also... it's always good to have the original signature iter_parent(iter : Gtk::TreeIter, child : Gtk::TreeIter), so when doing a lot of operations we can share the TreeIter object avoid some allocations.

BTW, my pet project is now public, I keep a gtk.cr[1] file with changes to make GTK API nicer... I kept the changes there hope to identify some pattern, then submit something here... and also to avoid to send the first API change that comes in my mind. I took a look into Vala API, but it's practically the same verbose C API, guess would be more useful to borrow ideas from Ruby and Python bindings.

https://github.com/hugopl/tijolo/blob/master/src/gtk.cr

Alright, with the two commits above the generator should generate g_content_type_guess now almost exactly as you did manually, only change is taking a Bytes? for the data argument.

I left non "primitive" types alone for now (stringy out args are also handled.) Arrays of things should be doable but I'd like to verify that with an actual usecase at hand.

I actually expected a lot more things in your gtk.cr, I guess that it's so short is a good sign :D