ref_function from vapi is used as dup function even if ref_function_void is set
ref_function_void = true
indicates that the ref function, contrary to gobject convention, returns void. This means it can't be directly used as a copy/dup function which expects the return value to return the pointer of the copy (or the pointer itself but ref increased).
The solution is to automatically generate a helper function when a copy/dup function for a class with ref_function_void = true
is needed.
static <type> *_<type-ref-function>_dup (<type> *var) {
if (var != NULL) <type-ref-function> (var);
return var;
}
GPGME has ref/unref functions that return void. We currently have a c file with those helpers in our code and adjusted the vapi to use those instead of directly calling the variant from GPGME and then we just unset the ref_function_void = true
. Still, it would be much better if this was handled by the vala compiler.
Reproducer
ref_void_c.vapi
:
[Compact]
[CCode (ref_function = "foo_ref", unref_function = "foo_unref", ref_function_void = true, cheader_filename = "ref_void_c.h")]
public class Foo {
public Foo (string val);
public string val { get; }
}
ref_void_c.h
:
#ifndef REF_VOID_C_H
#define REF_VOID_C_H
typedef struct Foo {
int ref;
char *val;
} Foo;
Foo *foo_new(char *val);
void foo_ref(Foo *foo);
void foo_unref(Foo *foo);
char *foo_get_val(Foo *foo);
#endif
ref_void_c.c
:
#include <string.h>
#include <stdlib.h>
#include "ref_void_c.h"
Foo *foo_new(char *val) {
Foo *foo = (Foo*) malloc(sizeof(Foo));
foo->ref = 1;
foo->val = val;
return foo;
}
void foo_ref(Foo *foo) {
foo->ref++;
}
void foo_unref(Foo *foo) {
foo->ref--;
if (foo->ref <= 0) {
memset(foo, 0, sizeof(Foo));
free(foo);
}
}
char *foo_get_val(Foo *foo) {
return foo->val;
}
ref_void.vala
:
public static void main (string[] args) {
// Putting Foo as a type argument means we use Foo's ref/unref function as dup/destroy function.
// The C code will cast foo_ref to GBoxedCopyFunc, which has an incompatible function signature
Gee.List<Foo> list = new Gee.ArrayList<Foo> ();
list.add (new Foo ("Test\n"));
print (list[0].val);
}
Compile and execute:
valac --pkg=gee-0.8 -X -O2 ref_void.vala ref_void_c.vapi ref_void_c.c; ./ref_void
Crashes, because list[0] does not contain a valid value (but the return value of foo_ref, which is undefined behavior).