1 | /* |
2 | * This file is part of the MicroPython project, http://micropython.org/ |
3 | * |
4 | * The MIT License (MIT) |
5 | * |
6 | * Copyright (c) 2013, 2014 Damien P. George |
7 | * Copyright (c) 2014-2018 Paul Sokolovsky |
8 | * |
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | * of this software and associated documentation files (the "Software"), to deal |
11 | * in the Software without restriction, including without limitation the rights |
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | * copies of the Software, and to permit persons to whom the Software is |
14 | * furnished to do so, subject to the following conditions: |
15 | * |
16 | * The above copyright notice and this permission notice shall be included in |
17 | * all copies or substantial portions of the Software. |
18 | * |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | * THE SOFTWARE. |
26 | */ |
27 | |
28 | #include <assert.h> |
29 | #include <string.h> |
30 | #include <errno.h> |
31 | #include <dlfcn.h> |
32 | #include <ffi.h> |
33 | #include <stdint.h> |
34 | |
35 | #include "py/runtime.h" |
36 | #include "py/binary.h" |
37 | #include "py/mperrno.h" |
38 | |
39 | /* |
40 | * modffi uses character codes to encode a value type, based on "struct" |
41 | * module type codes, with some extensions and overridings. |
42 | * |
43 | * Extra/overridden typecodes: |
44 | * v - void, can be used only as return type |
45 | * P - const void*, pointer to read-only memory |
46 | * p - void*, meaning pointer to a writable memory (note that this |
47 | * clashes with struct's "p" as "Pascal string"). |
48 | * s - as argument, the same as "p", as return value, causes string |
49 | * to be allocated and returned, instead of pointer value. |
50 | * O - mp_obj_t, passed as is (mostly useful as a callback param) |
51 | * |
52 | * TODO: |
53 | * C - callback function |
54 | * |
55 | * Note: all constraint specified by typecode can be not enforced at this time, |
56 | * but may be later. |
57 | */ |
58 | |
59 | typedef struct _mp_obj_opaque_t { |
60 | mp_obj_base_t base; |
61 | void *val; |
62 | } mp_obj_opaque_t; |
63 | |
64 | typedef struct _mp_obj_ffimod_t { |
65 | mp_obj_base_t base; |
66 | void *handle; |
67 | } mp_obj_ffimod_t; |
68 | |
69 | typedef struct _mp_obj_ffivar_t { |
70 | mp_obj_base_t base; |
71 | void *var; |
72 | char type; |
73 | // ffi_type *type; |
74 | } mp_obj_ffivar_t; |
75 | |
76 | typedef struct _mp_obj_ffifunc_t { |
77 | mp_obj_base_t base; |
78 | void *func; |
79 | char rettype; |
80 | const char *argtypes; |
81 | ffi_cif cif; |
82 | ffi_type *params[]; |
83 | } mp_obj_ffifunc_t; |
84 | |
85 | typedef struct _mp_obj_fficallback_t { |
86 | mp_obj_base_t base; |
87 | void *func; |
88 | ffi_closure *clo; |
89 | char rettype; |
90 | ffi_cif cif; |
91 | ffi_type *params[]; |
92 | } mp_obj_fficallback_t; |
93 | |
94 | // STATIC const mp_obj_type_t opaque_type; |
95 | STATIC const mp_obj_type_t ffimod_type; |
96 | STATIC const mp_obj_type_t ffifunc_type; |
97 | STATIC const mp_obj_type_t fficallback_type; |
98 | STATIC const mp_obj_type_t ffivar_type; |
99 | |
100 | STATIC ffi_type *char2ffi_type(char c) { |
101 | switch (c) { |
102 | case 'b': |
103 | return &ffi_type_schar; |
104 | case 'B': |
105 | return &ffi_type_uchar; |
106 | case 'h': |
107 | return &ffi_type_sshort; |
108 | case 'H': |
109 | return &ffi_type_ushort; |
110 | case 'i': |
111 | return &ffi_type_sint; |
112 | case 'I': |
113 | return &ffi_type_uint; |
114 | case 'l': |
115 | return &ffi_type_slong; |
116 | case 'L': |
117 | return &ffi_type_ulong; |
118 | case 'q': |
119 | return &ffi_type_sint64; |
120 | case 'Q': |
121 | return &ffi_type_uint64; |
122 | #if MICROPY_PY_BUILTINS_FLOAT |
123 | case 'f': |
124 | return &ffi_type_float; |
125 | case 'd': |
126 | return &ffi_type_double; |
127 | #endif |
128 | case 'O': // mp_obj_t |
129 | case 'C': // (*)() |
130 | case 'P': // const void* |
131 | case 'p': // void* |
132 | case 's': |
133 | return &ffi_type_pointer; |
134 | case 'v': |
135 | return &ffi_type_void; |
136 | default: |
137 | return NULL; |
138 | } |
139 | } |
140 | |
141 | STATIC ffi_type *get_ffi_type(mp_obj_t o_in) { |
142 | if (mp_obj_is_str(o_in)) { |
143 | const char *s = mp_obj_str_get_str(o_in); |
144 | ffi_type *t = char2ffi_type(*s); |
145 | if (t != NULL) { |
146 | return t; |
147 | } |
148 | } |
149 | // TODO: Support actual libffi type objects |
150 | |
151 | mp_raise_TypeError(MP_ERROR_TEXT("unknown type" )); |
152 | } |
153 | |
154 | STATIC mp_obj_t return_ffi_value(ffi_arg val, char type) { |
155 | switch (type) { |
156 | case 's': { |
157 | const char *s = (const char *)(intptr_t)val; |
158 | if (!s) { |
159 | return mp_const_none; |
160 | } |
161 | return mp_obj_new_str(s, strlen(s)); |
162 | } |
163 | case 'v': |
164 | return mp_const_none; |
165 | #if MICROPY_PY_BUILTINS_FLOAT |
166 | case 'f': { |
167 | union { ffi_arg ffi; |
168 | float flt; |
169 | } val_union = { .ffi = val }; |
170 | return mp_obj_new_float_from_f(val_union.flt); |
171 | } |
172 | case 'd': { |
173 | double *p = (double *)&val; |
174 | return mp_obj_new_float_from_d(*p); |
175 | } |
176 | #endif |
177 | case 'O': |
178 | return (mp_obj_t)(intptr_t)val; |
179 | default: |
180 | return mp_obj_new_int(val); |
181 | } |
182 | } |
183 | |
184 | // FFI module |
185 | |
186 | STATIC void ffimod_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
187 | (void)kind; |
188 | mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); |
189 | mp_printf(print, "<ffimod %p>" , self->handle); |
190 | } |
191 | |
192 | STATIC mp_obj_t ffimod_close(mp_obj_t self_in) { |
193 | mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); |
194 | dlclose(self->handle); |
195 | return mp_const_none; |
196 | } |
197 | STATIC MP_DEFINE_CONST_FUN_OBJ_1(ffimod_close_obj, ffimod_close); |
198 | |
199 | STATIC mp_obj_t make_func(mp_obj_t rettype_in, void *func, mp_obj_t argtypes_in) { |
200 | const char *rettype = mp_obj_str_get_str(rettype_in); |
201 | const char *argtypes = mp_obj_str_get_str(argtypes_in); |
202 | |
203 | mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(argtypes_in)); |
204 | mp_obj_ffifunc_t *o = m_new_obj_var(mp_obj_ffifunc_t, ffi_type *, nparams); |
205 | o->base.type = &ffifunc_type; |
206 | |
207 | o->func = func; |
208 | o->rettype = *rettype; |
209 | o->argtypes = argtypes; |
210 | |
211 | mp_obj_iter_buf_t iter_buf; |
212 | mp_obj_t iterable = mp_getiter(argtypes_in, &iter_buf); |
213 | mp_obj_t item; |
214 | int i = 0; |
215 | while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { |
216 | o->params[i++] = get_ffi_type(item); |
217 | } |
218 | |
219 | int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params); |
220 | if (res != FFI_OK) { |
221 | mp_raise_ValueError(MP_ERROR_TEXT("error in ffi_prep_cif" )); |
222 | } |
223 | |
224 | return MP_OBJ_FROM_PTR(o); |
225 | } |
226 | |
227 | STATIC mp_obj_t ffimod_func(size_t n_args, const mp_obj_t *args) { |
228 | (void)n_args; // always 4 |
229 | mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(args[0]); |
230 | const char *symname = mp_obj_str_get_str(args[2]); |
231 | |
232 | void *sym = dlsym(self->handle, symname); |
233 | if (sym == NULL) { |
234 | mp_raise_OSError(MP_ENOENT); |
235 | } |
236 | return make_func(args[1], sym, args[3]); |
237 | } |
238 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ffimod_func_obj, 4, 4, ffimod_func); |
239 | |
240 | STATIC mp_obj_t mod_ffi_func(mp_obj_t rettype, mp_obj_t addr_in, mp_obj_t argtypes) { |
241 | void *addr = (void *)MP_OBJ_TO_PTR(mp_obj_int_get_truncated(addr_in)); |
242 | return make_func(rettype, addr, argtypes); |
243 | } |
244 | MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_func_obj, mod_ffi_func); |
245 | |
246 | STATIC void call_py_func(ffi_cif *cif, void *ret, void **args, void *func) { |
247 | mp_obj_t pyargs[cif->nargs]; |
248 | for (uint i = 0; i < cif->nargs; i++) { |
249 | pyargs[i] = mp_obj_new_int(*(mp_int_t *)args[i]); |
250 | } |
251 | mp_obj_t res = mp_call_function_n_kw(MP_OBJ_FROM_PTR(func), cif->nargs, 0, pyargs); |
252 | |
253 | if (res != mp_const_none) { |
254 | *(ffi_arg *)ret = mp_obj_int_get_truncated(res); |
255 | } |
256 | } |
257 | |
258 | STATIC mp_obj_t mod_ffi_callback(mp_obj_t rettype_in, mp_obj_t func_in, mp_obj_t paramtypes_in) { |
259 | const char *rettype = mp_obj_str_get_str(rettype_in); |
260 | |
261 | mp_int_t nparams = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(paramtypes_in)); |
262 | mp_obj_fficallback_t *o = m_new_obj_var(mp_obj_fficallback_t, ffi_type *, nparams); |
263 | o->base.type = &fficallback_type; |
264 | |
265 | o->clo = ffi_closure_alloc(sizeof(ffi_closure), &o->func); |
266 | |
267 | o->rettype = *rettype; |
268 | |
269 | mp_obj_iter_buf_t iter_buf; |
270 | mp_obj_t iterable = mp_getiter(paramtypes_in, &iter_buf); |
271 | mp_obj_t item; |
272 | int i = 0; |
273 | while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { |
274 | o->params[i++] = get_ffi_type(item); |
275 | } |
276 | |
277 | int res = ffi_prep_cif(&o->cif, FFI_DEFAULT_ABI, nparams, char2ffi_type(*rettype), o->params); |
278 | if (res != FFI_OK) { |
279 | mp_raise_ValueError(MP_ERROR_TEXT("error in ffi_prep_cif" )); |
280 | } |
281 | |
282 | res = ffi_prep_closure_loc(o->clo, &o->cif, call_py_func, MP_OBJ_TO_PTR(func_in), o->func); |
283 | if (res != FFI_OK) { |
284 | mp_raise_ValueError(MP_ERROR_TEXT("ffi_prep_closure_loc" )); |
285 | } |
286 | |
287 | return MP_OBJ_FROM_PTR(o); |
288 | } |
289 | MP_DEFINE_CONST_FUN_OBJ_3(mod_ffi_callback_obj, mod_ffi_callback); |
290 | |
291 | STATIC mp_obj_t ffimod_var(mp_obj_t self_in, mp_obj_t vartype_in, mp_obj_t symname_in) { |
292 | mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); |
293 | const char *rettype = mp_obj_str_get_str(vartype_in); |
294 | const char *symname = mp_obj_str_get_str(symname_in); |
295 | |
296 | void *sym = dlsym(self->handle, symname); |
297 | if (sym == NULL) { |
298 | mp_raise_OSError(MP_ENOENT); |
299 | } |
300 | mp_obj_ffivar_t *o = m_new_obj(mp_obj_ffivar_t); |
301 | o->base.type = &ffivar_type; |
302 | |
303 | o->var = sym; |
304 | o->type = *rettype; |
305 | return MP_OBJ_FROM_PTR(o); |
306 | } |
307 | MP_DEFINE_CONST_FUN_OBJ_3(ffimod_var_obj, ffimod_var); |
308 | |
309 | STATIC mp_obj_t ffimod_addr(mp_obj_t self_in, mp_obj_t symname_in) { |
310 | mp_obj_ffimod_t *self = MP_OBJ_TO_PTR(self_in); |
311 | const char *symname = mp_obj_str_get_str(symname_in); |
312 | |
313 | void *sym = dlsym(self->handle, symname); |
314 | if (sym == NULL) { |
315 | mp_raise_OSError(MP_ENOENT); |
316 | } |
317 | return mp_obj_new_int((uintptr_t)sym); |
318 | } |
319 | MP_DEFINE_CONST_FUN_OBJ_2(ffimod_addr_obj, ffimod_addr); |
320 | |
321 | STATIC mp_obj_t ffimod_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { |
322 | (void)n_args; |
323 | (void)n_kw; |
324 | |
325 | const char *fname = NULL; |
326 | if (args[0] != mp_const_none) { |
327 | fname = mp_obj_str_get_str(args[0]); |
328 | } |
329 | void *mod = dlopen(fname, RTLD_NOW | RTLD_LOCAL); |
330 | |
331 | if (mod == NULL) { |
332 | mp_raise_OSError(errno); |
333 | } |
334 | mp_obj_ffimod_t *o = m_new_obj(mp_obj_ffimod_t); |
335 | o->base.type = type; |
336 | o->handle = mod; |
337 | return MP_OBJ_FROM_PTR(o); |
338 | } |
339 | |
340 | STATIC const mp_rom_map_elem_t ffimod_locals_dict_table[] = { |
341 | { MP_ROM_QSTR(MP_QSTR_func), MP_ROM_PTR(&ffimod_func_obj) }, |
342 | { MP_ROM_QSTR(MP_QSTR_var), MP_ROM_PTR(&ffimod_var_obj) }, |
343 | { MP_ROM_QSTR(MP_QSTR_addr), MP_ROM_PTR(&ffimod_addr_obj) }, |
344 | { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&ffimod_close_obj) }, |
345 | }; |
346 | |
347 | STATIC MP_DEFINE_CONST_DICT(ffimod_locals_dict, ffimod_locals_dict_table); |
348 | |
349 | STATIC const mp_obj_type_t ffimod_type = { |
350 | { &mp_type_type }, |
351 | .name = MP_QSTR_ffimod, |
352 | .print = ffimod_print, |
353 | .make_new = ffimod_make_new, |
354 | .locals_dict = (mp_obj_dict_t *)&ffimod_locals_dict, |
355 | }; |
356 | |
357 | // FFI function |
358 | |
359 | STATIC void ffifunc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
360 | (void)kind; |
361 | mp_obj_ffifunc_t *self = MP_OBJ_TO_PTR(self_in); |
362 | mp_printf(print, "<ffifunc %p>" , self->func); |
363 | } |
364 | |
365 | STATIC mp_obj_t ffifunc_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { |
366 | (void)n_kw; |
367 | mp_obj_ffifunc_t *self = MP_OBJ_TO_PTR(self_in); |
368 | assert(n_kw == 0); |
369 | assert(n_args == self->cif.nargs); |
370 | |
371 | ffi_arg values[n_args]; |
372 | void *valueptrs[n_args]; |
373 | const char *argtype = self->argtypes; |
374 | for (uint i = 0; i < n_args; i++, argtype++) { |
375 | mp_obj_t a = args[i]; |
376 | if (*argtype == 'O') { |
377 | values[i] = (ffi_arg)(intptr_t)a; |
378 | #if MICROPY_PY_BUILTINS_FLOAT |
379 | } else if (*argtype == 'f') { |
380 | float *p = (float *)&values[i]; |
381 | *p = mp_obj_get_float_to_f(a); |
382 | } else if (*argtype == 'd') { |
383 | double *p = (double *)&values[i]; |
384 | *p = mp_obj_get_float_to_d(a); |
385 | #endif |
386 | } else if (a == mp_const_none) { |
387 | values[i] = 0; |
388 | } else if (mp_obj_is_int(a)) { |
389 | values[i] = mp_obj_int_get_truncated(a); |
390 | } else if (mp_obj_is_str(a)) { |
391 | const char *s = mp_obj_str_get_str(a); |
392 | values[i] = (ffi_arg)(intptr_t)s; |
393 | } else if (((mp_obj_base_t *)MP_OBJ_TO_PTR(a))->type->buffer_p.get_buffer != NULL) { |
394 | mp_obj_base_t *o = (mp_obj_base_t *)MP_OBJ_TO_PTR(a); |
395 | mp_buffer_info_t bufinfo; |
396 | int ret = o->type->buffer_p.get_buffer(MP_OBJ_FROM_PTR(o), &bufinfo, MP_BUFFER_READ); // TODO: MP_BUFFER_READ? |
397 | if (ret != 0) { |
398 | goto error; |
399 | } |
400 | values[i] = (ffi_arg)(intptr_t)bufinfo.buf; |
401 | } else if (mp_obj_is_type(a, &fficallback_type)) { |
402 | mp_obj_fficallback_t *p = MP_OBJ_TO_PTR(a); |
403 | values[i] = (ffi_arg)(intptr_t)p->func; |
404 | } else { |
405 | goto error; |
406 | } |
407 | valueptrs[i] = &values[i]; |
408 | } |
409 | |
410 | // If ffi_arg is not big enough to hold a double, then we must pass along a |
411 | // pointer to a memory location of the correct size. |
412 | // TODO check if this needs to be done for other types which don't fit into |
413 | // ffi_arg. |
414 | #if MICROPY_PY_BUILTINS_FLOAT |
415 | if (sizeof(ffi_arg) == 4 && self->rettype == 'd') { |
416 | double retval; |
417 | ffi_call(&self->cif, self->func, &retval, valueptrs); |
418 | return mp_obj_new_float_from_d(retval); |
419 | } else |
420 | #endif |
421 | { |
422 | ffi_arg retval; |
423 | ffi_call(&self->cif, self->func, &retval, valueptrs); |
424 | return return_ffi_value(retval, self->rettype); |
425 | } |
426 | |
427 | error: |
428 | mp_raise_TypeError(MP_ERROR_TEXT("don't know how to pass object to native function" )); |
429 | } |
430 | |
431 | STATIC const mp_obj_type_t ffifunc_type = { |
432 | { &mp_type_type }, |
433 | .name = MP_QSTR_ffifunc, |
434 | .print = ffifunc_print, |
435 | .call = ffifunc_call, |
436 | }; |
437 | |
438 | // FFI callback for Python function |
439 | |
440 | STATIC void fficallback_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
441 | (void)kind; |
442 | mp_obj_fficallback_t *self = MP_OBJ_TO_PTR(self_in); |
443 | mp_printf(print, "<fficallback %p>" , self->func); |
444 | } |
445 | |
446 | STATIC const mp_obj_type_t fficallback_type = { |
447 | { &mp_type_type }, |
448 | .name = MP_QSTR_fficallback, |
449 | .print = fficallback_print, |
450 | }; |
451 | |
452 | // FFI variable |
453 | |
454 | STATIC void ffivar_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { |
455 | (void)kind; |
456 | mp_obj_ffivar_t *self = MP_OBJ_TO_PTR(self_in); |
457 | // Variable value printed as cast to int |
458 | mp_printf(print, "<ffivar @%p: 0x%x>" , self->var, *(int *)self->var); |
459 | } |
460 | |
461 | STATIC mp_obj_t ffivar_get(mp_obj_t self_in) { |
462 | mp_obj_ffivar_t *self = MP_OBJ_TO_PTR(self_in); |
463 | return mp_binary_get_val_array(self->type, self->var, 0); |
464 | } |
465 | MP_DEFINE_CONST_FUN_OBJ_1(ffivar_get_obj, ffivar_get); |
466 | |
467 | STATIC mp_obj_t ffivar_set(mp_obj_t self_in, mp_obj_t val_in) { |
468 | mp_obj_ffivar_t *self = MP_OBJ_TO_PTR(self_in); |
469 | mp_binary_set_val_array(self->type, self->var, 0, val_in); |
470 | return mp_const_none; |
471 | } |
472 | MP_DEFINE_CONST_FUN_OBJ_2(ffivar_set_obj, ffivar_set); |
473 | |
474 | STATIC const mp_rom_map_elem_t ffivar_locals_dict_table[] = { |
475 | { MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&ffivar_get_obj) }, |
476 | { MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&ffivar_set_obj) }, |
477 | }; |
478 | |
479 | STATIC MP_DEFINE_CONST_DICT(ffivar_locals_dict, ffivar_locals_dict_table); |
480 | |
481 | STATIC const mp_obj_type_t ffivar_type = { |
482 | { &mp_type_type }, |
483 | .name = MP_QSTR_ffivar, |
484 | .print = ffivar_print, |
485 | .locals_dict = (mp_obj_dict_t *)&ffivar_locals_dict, |
486 | }; |
487 | |
488 | // Generic opaque storage object (unused) |
489 | |
490 | /* |
491 | STATIC const mp_obj_type_t opaque_type = { |
492 | { &mp_type_type }, |
493 | .name = MP_QSTR_opaqueval, |
494 | // .print = opaque_print, |
495 | }; |
496 | */ |
497 | |
498 | STATIC mp_obj_t mod_ffi_open(size_t n_args, const mp_obj_t *args) { |
499 | return ffimod_make_new(&ffimod_type, n_args, 0, args); |
500 | } |
501 | MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_ffi_open_obj, 1, 2, mod_ffi_open); |
502 | |
503 | STATIC mp_obj_t mod_ffi_as_bytearray(mp_obj_t ptr, mp_obj_t size) { |
504 | return mp_obj_new_bytearray_by_ref(mp_obj_int_get_truncated(size), (void *)(uintptr_t)mp_obj_int_get_truncated(ptr)); |
505 | } |
506 | MP_DEFINE_CONST_FUN_OBJ_2(mod_ffi_as_bytearray_obj, mod_ffi_as_bytearray); |
507 | |
508 | STATIC const mp_rom_map_elem_t mp_module_ffi_globals_table[] = { |
509 | { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ffi) }, |
510 | { MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_ffi_open_obj) }, |
511 | { MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&mod_ffi_callback_obj) }, |
512 | { MP_ROM_QSTR(MP_QSTR_func), MP_ROM_PTR(&mod_ffi_func_obj) }, |
513 | { MP_ROM_QSTR(MP_QSTR_as_bytearray), MP_ROM_PTR(&mod_ffi_as_bytearray_obj) }, |
514 | }; |
515 | |
516 | STATIC MP_DEFINE_CONST_DICT(mp_module_ffi_globals, mp_module_ffi_globals_table); |
517 | |
518 | const mp_obj_module_t mp_module_ffi = { |
519 | .base = { &mp_type_module }, |
520 | .globals = (mp_obj_dict_t *)&mp_module_ffi_globals, |
521 | }; |
522 | |