summaryrefslogtreecommitdiff
path: root/c/call_python.c
blob: 4db966ad174e8cf51ed780b15da56e4ba9991c9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn)
{
#if PY_MAJOR_VERSION >= 3
#  error review!
#endif
    char *s;
    PyObject *error, *onerror, *infotuple, *x;
    int index;
    const struct _cffi_global_s *g;
    struct _cffi_externpy_s *externpy;
    CTypeDescrObject *ct;
    FFIObject *ffi;
    builder_c_t *types_builder;
    PyObject *name = NULL;

    if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror))
        return NULL;

    if (s == NULL) {
        PyObject *name = PyObject_GetAttrString(fn, "__name__");
        if (name == NULL)
            return NULL;
        s = PyString_AsString(name);
        if (s == NULL) {
            Py_DECREF(name);
            return NULL;
        }
    }

    types_builder = &ffi->types_builder;
    index = search_in_globals(&types_builder->ctx, s, strlen(s));
    if (index < 0)
        goto not_found;
    g = &types_builder->ctx.globals[index];
    if (_CFFI_GETOP(g->type_op) != _CFFI_OP_EXTERN_PYTHON)
        goto not_found;
    Py_XDECREF(name);

    ct = realize_c_type(types_builder, types_builder->ctx.types,
                        _CFFI_GETARG(g->type_op));
    if (ct == NULL)
        return NULL;

    infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0);
    if (infotuple == NULL) {
        Py_DECREF(ct);
        return NULL;
    }

    /* attach infotuple to reserved1, where it will stay forever
       unless a new version is attached later */
    externpy = (struct _cffi_externpy_s *)g->address;
    x = (PyObject *)externpy->reserved1;
    externpy->reserved1 = (void *)infotuple;
    Py_XDECREF(x);

    /* return a cdata of type function-pointer, equal to the one
       obtained by reading 'lib.bar' (see lib_obj.c) */
    x = convert_to_object((char *)&g->size_or_direct_fn, ct);
    Py_DECREF(ct);
    return x;

 not_found:
    PyErr_Format(FFIError, "ffi.def_extern('%s'): no 'extern \"Python\"' "
                 "function with this name", s);
    Py_XDECREF(name);
    return NULL;
}


static void _cffi_call_python(struct _cffi_externpy_s *externpy, char *args)
{
    /* Invoked by the helpers generated from extern "Python" in the cdef.

       'externpy' is a static structure that describes which of the
       extern "Python" functions is called.  It has got fields 'name' and
       'type_index' describing the function, and more reserved fields
       that are initially zero.  These reserved fields are set up by
       ffi.def_extern(), which invokes _ffi_def_extern_decorator() above.

       'args' is a pointer to an array of 8-byte entries.  Each entry
       contains an argument.  If an argument is less than 8 bytes, only
       the part at the beginning of the entry is initialized.  If an
       argument is 'long double' or a struct/union, then it is passed
       by reference.

       'args' is also used as the place to write the result to
       (directly, even if more than 8 bytes).  In all cases, 'args' is
       at least 8 bytes in size.
    */
    save_errno();

    if (externpy->reserved1 == NULL) {
        /* not initialized! */
        fprintf(stderr, "extern \"Python\": function %s() called, "
                        "but no code was attached to it yet with "
                        "@ffi.def_extern().  Returning 0.\n", externpy->name);
        memset(args, 0, externpy->size_of_result);
    }
    else {
#ifdef WITH_THREAD
        PyGILState_STATE state = PyGILState_Ensure();
#endif
        general_invoke_callback(0, args, args, externpy->reserved1);
#ifdef WITH_THREAD
        PyGILState_Release(state);
#endif
    }
    restore_errno();
}