ffi.c 10.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <ffi.h>

#include "nlr.h"
#include "misc.h"
#include "mpconfig.h"
#include "qstr.h"
#include "obj.h"
12
#include "map.h"
13
#include "runtime.h"
14
#include "binary.h"
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

typedef struct _mp_obj_opaque_t {
    mp_obj_base_t base;
    void *val;
} mp_obj_opaque_t;

typedef struct _mp_obj_ffimod_t {
    mp_obj_base_t base;
    void *handle;
} mp_obj_ffimod_t;

typedef struct _mp_obj_ffivar_t {
    mp_obj_base_t base;
    void *var;
    char type;
//    ffi_type *type;
} mp_obj_ffivar_t;

typedef struct _mp_obj_ffifunc_t {
    mp_obj_base_t base;
    void *func;
    char rettype;
    ffi_cif cif;
    ffi_type *params[];
} mp_obj_ffifunc_t;

typedef struct _mp_obj_fficallback_t {
    mp_obj_base_t base;
    void *func;
    ffi_closure *clo;
    char rettype;
    ffi_cif cif;
    ffi_type *params[];
} mp_obj_fficallback_t;

50
51
52
53
54
//STATIC const mp_obj_type_t opaque_type;
STATIC const mp_obj_type_t ffimod_type;
STATIC const mp_obj_type_t ffifunc_type;
STATIC const mp_obj_type_t fficallback_type;
STATIC const mp_obj_type_t ffivar_type;
55

56
STATIC ffi_type *char2ffi_type(char c)
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
{
    switch (c) {
        case 'b': return &ffi_type_schar;
        case 'B': return &ffi_type_uchar;
        case 'i': return &ffi_type_sint;
        case 'I': return &ffi_type_uint;
        case 'l': return &ffi_type_slong;
        case 'L': return &ffi_type_ulong;
        case 'p':
        case 's': return &ffi_type_pointer;
        case 'v': return &ffi_type_void;
        default: return NULL;
    }
}

72
STATIC ffi_type *get_ffi_type(mp_obj_t o_in)
73
74
75
{
    if (MP_OBJ_IS_STR(o_in)) {
        uint len;
76
        const char *s = mp_obj_str_get_data(o_in, &len);
77
78
79
80
81
82
83
        ffi_type *t = char2ffi_type(*s);
        if (t != NULL) {
            return t;
        }
    }
    // TODO: Support actual libffi type objects

84
    nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Unknown type"));
85
86
}

87
STATIC mp_obj_t return_ffi_value(ffi_arg val, char type)
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{
    switch (type) {
        case 's': {
            const char *s = (const char *)val;
            return mp_obj_new_str((const byte *)s, strlen(s), false);
        }
        case 'v':
            return mp_const_none;
        default:
            return mp_obj_new_int(val);
    }
}

// FFI module

103
STATIC void ffimod_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
104
105
106
107
    mp_obj_ffimod_t *self = self_in;
    print(env, "<ffimod %p>", self->handle);
}

108
STATIC mp_obj_t ffimod_close(mp_obj_t self_in) {
109
110
111
112
    mp_obj_ffimod_t *self = self_in;
    dlclose(self->handle);
    return mp_const_none;
}
113
STATIC MP_DEFINE_CONST_FUN_OBJ_1(ffimod_close_obj, ffimod_close);
114

115
STATIC mp_obj_t ffimod_func(uint n_args, const mp_obj_t *args) {
116
117
118
119
120
121
    mp_obj_ffimod_t *self = args[0];
    const char *rettype = mp_obj_str_get_str(args[1]);
    const char *symname = mp_obj_str_get_str(args[2]);

    void *sym = dlsym(self->handle, symname);
    if (sym == NULL) {
122
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "[Errno %d]", errno));
123
124
125
126
127
128
129
130
131
132
133
    }
    int nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(args[3]));
    mp_obj_ffifunc_t *o = m_new_obj_var(mp_obj_ffifunc_t, ffi_type*, nparams);
    o->base.type = &ffifunc_type;

    o->func = sym;
    o->rettype = *rettype;

    mp_obj_t iterable = rt_getiter(args[3]);
    mp_obj_t item;
    int i = 0;
134
    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
135
136
137
138
139
        o->params[i++] = get_ffi_type(item);
    }

    int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params);
    if (res != FFI_OK) {
140
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error in ffi_prep_cif"));
141
142
143
144
145
146
    }

    return o;
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ffimod_func_obj, 4, 4, ffimod_func);

147
STATIC void call_py_func(ffi_cif *cif, void *ret, void** args, mp_obj_t func) {
148
149
150
151
152
153
154
155
156
    mp_obj_t pyargs[cif->nargs];
    for (int i = 0; i < cif->nargs; i++) {
        pyargs[i] = mp_obj_new_int(*(int*)args[i]);
    }
    mp_obj_t res = rt_call_function_n_kw(func, cif->nargs, 0, pyargs);

    *(ffi_arg*)ret = mp_obj_int_get(res);
}

157
STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t paramtypes_in) {
158
159
160
161
162
163
164
165
166
167
168
169
170
    const char *rettype = mp_obj_str_get_str(rettype_in);

    int nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(paramtypes_in));
    mp_obj_fficallback_t *o = m_new_obj_var(mp_obj_fficallback_t, ffi_type*, nparams);
    o->base.type = &fficallback_type;

    o->clo = ffi_closure_alloc(sizeof(ffi_closure), &o->func);

    o->rettype = *rettype;

    mp_obj_t iterable = rt_getiter(paramtypes_in);
    mp_obj_t item;
    int i = 0;
171
    while ((item = rt_iternext(iterable)) != MP_OBJ_NULL) {
172
173
174
175
176
        o->params[i++] = get_ffi_type(item);
    }

    int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params);
    if (res != FFI_OK) {
177
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "Error in ffi_prep_cif"));
178
179
180
181
    }

    res = ffi_prep_closure_loc(o->clo, &o->cif, call_py_func, func_in, o->func);
    if (res != FFI_OK) {
182
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "ffi_prep_closure_loc"));
183
184
185
186
187
188
    }

    return o;
}
MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_callback_obj, mod_ffi_callback);

189
STATIC mp_obj_t ffimod_var(mp_obj_t self_in, mp_obj_t vartype_in, mp_obj_t symname_in) {
190
191
192
193
194
195
    mp_obj_ffimod_t *self = self_in;
    const char *rettype = mp_obj_str_get_str(vartype_in);
    const char *symname = mp_obj_str_get_str(symname_in);

    void *sym = dlsym(self->handle, symname);
    if (sym == NULL) {
196
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "[Errno %d]", errno));
197
198
199
200
201
202
203
204
205
206
    }
    mp_obj_ffivar_t *o = m_new_obj(mp_obj_ffivar_t);
    o->base.type = &ffivar_type;

    o->var = sym;
    o->type = *rettype;
    return o;
}
MP_DEFINE_CONST_FUN_OBJ_3(ffimod_var_obj, ffimod_var);

207
STATIC mp_obj_t ffimod_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
208
209
210
211
    const char *fname = mp_obj_str_get_str(args[0]);
    void *mod = dlopen(fname, RTLD_NOW | RTLD_LOCAL);

    if (mod == NULL) {
212
        nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_OSError, "[Errno %d]", errno));
213
214
215
216
217
218
219
    }
    mp_obj_ffimod_t *o = m_new_obj(mp_obj_ffimod_t);
    o->base.type = type_in;
    o->handle = mod;
    return o;
}

220
STATIC const mp_map_elem_t ffimod_locals_dict_table[] = {
221
222
223
    { MP_OBJ_NEW_QSTR(MP_QSTR_func), (mp_obj_t) &ffimod_func_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_var), (mp_obj_t) &ffimod_var_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t) &ffimod_close_obj },
224
225
};

226
227
STATIC MP_DEFINE_CONST_DICT(ffimod_locals_dict, ffimod_locals_dict_table);

228
STATIC const mp_obj_type_t ffimod_type = {
229
230
    { &mp_type_type },
    .name = MP_QSTR_ffimod,
231
232
    .print = ffimod_print,
    .make_new = ffimod_make_new,
233
    .locals_dict = (mp_obj_t)&ffimod_locals_dict,
234
235
236
237
};

// FFI function

238
STATIC void ffifunc_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
239
240
241
242
243
244
245
246
247
    mp_obj_ffifunc_t *self = self_in;
    print(env, "<ffifunc %p>", self->func);
}

mp_obj_t ffifunc_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
    mp_obj_ffifunc_t *self = self_in;
    assert(n_kw == 0);
    assert(n_args == self->cif.nargs);

248
    ffi_arg values[n_args];
249
250
251
252
253
254
255
256
    void *valueptrs[n_args];
    int i;
    for (i = 0; i < n_args; i++) {
        mp_obj_t a = args[i];
        if (a == mp_const_none) {
            values[i] = 0;
        } else if (MP_OBJ_IS_INT(a)) {
            values[i] = mp_obj_int_get(a);
257
        } else if (MP_OBJ_IS_STR(a) || MP_OBJ_IS_TYPE(a, &mp_type_bytes)) {
258
            const char *s = mp_obj_str_get_str(a);
259
            values[i] = (ffi_arg)s;
260
261
        } else if (MP_OBJ_IS_TYPE(a, &fficallback_type)) {
            mp_obj_fficallback_t *p = a;
262
            values[i] = (ffi_arg)p->func;
263
264
265
266
267
268
        } else {
            assert(0);
        }
        valueptrs[i] = &values[i];
    }

269
    ffi_arg retval;
270
271
272
273
    ffi_call(&self->cif, self->func, &retval, valueptrs);
    return return_ffi_value(retval, self->rettype);
}

274
STATIC const mp_obj_type_t ffifunc_type = {
275
276
    { &mp_type_type },
    .name = MP_QSTR_ffifunc,
277
278
279
280
281
282
    .print = ffifunc_print,
    .call = ffifunc_call,
};

// FFI callback for Python function

283
STATIC void fficallback_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
284
285
286
287
    mp_obj_fficallback_t *self = self_in;
    print(env, "<fficallback %p>", self->func);
}

288
STATIC const mp_obj_type_t fficallback_type = {
289
290
    { &mp_type_type },
    .name = MP_QSTR_fficallback,
291
292
293
294
295
    .print = fficallback_print,
};

// FFI variable

296
STATIC void ffivar_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
297
298
299
300
    mp_obj_ffivar_t *self = self_in;
    print(env, "<ffivar @%p: 0x%x>", self->var, *(int*)self->var);
}

301
STATIC mp_obj_t ffivar_get(mp_obj_t self_in) {
302
303
304
305
306
    mp_obj_ffivar_t *self = self_in;
    return mp_binary_get_val(self->type, self->var, 0);
}
MP_DEFINE_CONST_FUN_OBJ_1(ffivar_get_obj, ffivar_get);

307
STATIC mp_obj_t ffivar_set(mp_obj_t self_in, mp_obj_t val_in) {
308
309
310
311
312
313
    mp_obj_ffivar_t *self = self_in;
    mp_binary_set_val(self->type, self->var, 0, val_in);
    return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(ffivar_set_obj, ffivar_set);

314
315
316
STATIC const mp_map_elem_t ffivar_locals_dict_table[] = {
    { MP_OBJ_NEW_QSTR(MP_QSTR_get), (mp_obj_t)&ffivar_get_obj },
    { MP_OBJ_NEW_QSTR(MP_QSTR_set), (mp_obj_t)&ffivar_set_obj },
317
318
};

319
320
321
STATIC MP_DEFINE_CONST_DICT(ffivar_locals_dict, ffivar_locals_dict_table);

STATIC const mp_obj_type_t ffivar_type = {
322
323
    { &mp_type_type },
    .name = MP_QSTR_ffivar,
324
    .print = ffivar_print,
325
    .locals_dict = (mp_obj_t)&ffivar_locals_dict,
326
327
};

328
// Generic opaque storage object (unused)
329

330
/*
331
STATIC const mp_obj_type_t opaque_type = {
332
333
    { &mp_type_type },
    .name = MP_QSTR_opaqueval,
334
335
//    .print = opaque_print,
};
336
*/
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355

mp_obj_t mod_ffi_open(uint n_args, const mp_obj_t *args) {
    return ffimod_make_new((mp_obj_t)&ffimod_type, n_args, 0, args);
}
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_ffi_open_obj, 1, 2, mod_ffi_open);

mp_obj_t mod_ffi_as_bytearray(mp_obj_t ptr, mp_obj_t size) {
    return mp_obj_new_bytearray_by_ref(mp_obj_int_get(size), (void*)mp_obj_int_get(ptr));
}
MP_DEFINE_CONST_FUN_OBJ_2(mod_ffi_as_bytearray_obj, mod_ffi_as_bytearray);


void ffi_init() {
    mp_obj_t m = mp_obj_new_module(QSTR_FROM_STR_STATIC("ffi"));
    rt_store_attr(m, MP_QSTR_open, (mp_obj_t)&mod_ffi_open_obj);
    rt_store_attr(m, QSTR_FROM_STR_STATIC("callback"), (mp_obj_t)&mod_ffi_callback_obj);
    // there would be as_bytes, but bytes currently is value, not reference type!
    rt_store_attr(m, QSTR_FROM_STR_STATIC("as_bytearray"), (mp_obj_t)&mod_ffi_as_bytearray_obj);
}