lgi-devs / lgi

Dynamic Lua binding to GObject libraries using GObject-Introspection
MIT License
440 stars 70 forks source link

`version` is used as a special field, but `Vips` library have function named this way #238

Open v1993 opened 4 years ago

v1993 commented 4 years ago

After having some not so fun time trying to debug this issue (it somehow worked right way first time and I have no idea why), it looks like I've found a problem regarding Vips library.

lgi allows to take a look at version of package loaded using field called version: https://github.com/pavouk/lgi/blob/master/lgi/gi.c#L680-L684

However, Vips library have function named this way (copied from /usr/share/gir-1.0/Vips-8.0.gir):

    <function name="version" c:identifier="vips_version">
      <doc xml:space="preserve"
           filename="iofuncs/init.c"
           line="1076">Get the major, minor or micro library version, with @flag values 0, 1 and
2.

Get the ABI current, revision and age (as used by libtool) with @flag
values 3, 4, 5.</doc>
      <source-position filename="include/vips/vips.h" line="178"/>
      <return-value transfer-ownership="none">
        <doc xml:space="preserve"
             filename="iofuncs/init.c"
             line="1086">library version number</doc>
        <type name="gint" c:type="int"/>
      </return-value>
      <parameters>
        <parameter name="flag" transfer-ownership="none">
          <doc xml:space="preserve"
               filename="iofuncs/init.c"
               line="1078">which field of the version to get</doc>
          <type name="gint" c:type="int"/>
        </parameter>
      </parameters>
    </function>

As a result, lgi overrides this function and returns its own string instead. Commenting out that part of code makes everything work as intended.

It worked first time I tried it (version was a function) and I don't know why: print(lgi.Vips:_resolve(true).version) still returns string value.

Are there any ways to work this around? Any ideas how it worked the first time?

I'd suggest to prefix all special names reserved by lgi with _ like you did with _resolve to avoid such collisions in the future, so it would be _version, _name, etc. If you agree with this, I can quickly submit a patch (they aren't documented anywhere, so it shouldn't be a big deal).

v1993 commented 4 years ago

A quick look suggests that there is no way to work this around as it is really the only place where g_irepository_find_by_name is called with user input.

psychon commented 4 years ago

Random quick idea: Short term, a workaround similar to what lgi/override/cairo.lua could be possible (or not). This basically uses a dlsym wrapper to look up symbols. Specifically, require("lgi.core").module('cairo', 2).cairo_version is used to look up the cairo_version symbol.

v1993 commented 4 years ago

@psychon It does something:

> require("lgi.core").module('vips').vips_version
userdata: 0x7f9a8ae146f0

So… how can I call this function?

psychon commented 4 years ago

The 0x7f9a8ae146f0 in your example is the raw address of the function. It does not get the function signature this way. I am not entirely sure how this works, but it might be to look at the cairo code, for example: https://github.com/pavouk/lgi/blob/ff50e59e85fe808a3bf6783005041449ec2a6bb8/lgi/override/cairo.lua#L62-L63 All of the cairo bindings are specified in this way (explicitly giving the function signature), because cairo is not GObject-based.

From https://jcupitt.github.io/libvips/API/8.5/libvips-vips.html#vips-version and a close look at this cairo.lua file, I would guess:

local core = require("lgi.core")
local ffi = require("lgi.ffi")
vips_version = core.callable.new {
  addr = core.module('vips').vips_version
  ret = ffi.types.int, ffi.types.int
}
print(vips_version(0))
v1993 commented 4 years ago

@psychon After fixing a typo it seems to work. Thank you! Actually, it wasn't a big hurry (I have more important things to port from that library).