Beispiel #1
0
static PyObject *ffi_fetch_int_constant(FFIObject *ffi, char *name,
                                        int recursion)
{
    int index;

    index = search_in_globals(&ffi->types_builder.ctx, name, strlen(name));
    if (index >= 0) {
        const struct _cffi_global_s *g;
        g = &ffi->types_builder.ctx.globals[index];

        switch (_CFFI_GETOP(g->type_op)) {
        case _CFFI_OP_CONSTANT_INT:
        case _CFFI_OP_ENUM:
            return realize_global_int(&ffi->types_builder, index);

        default:
            PyErr_Format(FFIError,
                         "function, global variable or non-integer constant "
                         "'%.200s' must be fetched from its original 'lib' "
                         "object", name);
            return NULL;
        }
    }

    if (ffi->types_builder.included_ffis != NULL) {
        Py_ssize_t i;
        PyObject *included_ffis = ffi->types_builder.included_ffis;

        if (recursion > 100) {
            PyErr_SetString(PyExc_RuntimeError,
                            "recursion overflow in ffi.include() delegations");
            return NULL;
        }

        for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) {
            FFIObject *ffi1;
            PyObject *x;

            ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i);
            x = ffi_fetch_int_constant(ffi1, name, recursion + 1);
            if (x != NULL || PyErr_Occurred())
                return x;
        }
    }
    return NULL;     /* no exception set, means "not found" */
}
Beispiel #2
0
static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds)
{
    FFIObject *ffi;
    static char *keywords[] = {"module_name", "_version", "_types",
                               "_globals", "_struct_unions", "_enums",
                               "_typenames", "_includes", NULL};
    char *ffiname = "?", *types = NULL, *building = NULL;
    Py_ssize_t version = -1;
    Py_ssize_t types_len = 0;
    PyObject *globals = NULL, *struct_unions = NULL, *enums = NULL;
    PyObject *typenames = NULL, *includes = NULL;

    if (!PyArg_ParseTupleAndKeywords(args, kwds,
                                     "|sns#O!O!O!O!O!:FFI", keywords,
                                     &ffiname, &version, &types, &types_len,
                                     &PyTuple_Type, &globals,
                                     &PyTuple_Type, &struct_unions,
                                     &PyTuple_Type, &enums,
                                     &PyTuple_Type, &typenames,
                                     &PyTuple_Type, &includes))
        return -1;

    ffi = (FFIObject *)self;
    if (ffi->ctx_is_nonempty) {
        PyErr_SetString(PyExc_ValueError,
                        "cannot call FFI.__init__() more than once");
        return -1;
    }
    ffi->ctx_is_nonempty = 1;

    if (version == -1 && types_len == 0)
        return 0;
    if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) {
        PyErr_Format(PyExc_ImportError,
                     "cffi out-of-line Python module '%s' has unknown "
                     "version %p", ffiname, (void *)version);
        return -1;
    }

    if (types_len > 0) {
        /* unpack a string of 4-byte entries into an array of _cffi_opcode_t */
        _cffi_opcode_t *ntypes;
        Py_ssize_t i, n = types_len / 4;

        building = PyMem_Malloc(n * sizeof(_cffi_opcode_t));
        if (building == NULL)
            goto error;
        ntypes = (_cffi_opcode_t *)building;

        for (i = 0; i < n; i++) {
            ntypes[i] = cdl_opcode(types);
            types += 4;
        }
        ffi->types_builder.ctx.types = ntypes;
        ffi->types_builder.ctx.num_types = n;
        building = NULL;
    }

    if (globals != NULL) {
        /* unpack a tuple alternating strings and ints, each two together
           describing one global_s entry with no specified address or size.
           The int is only used with integer constants. */
        struct _cffi_global_s *nglobs;
        cdl_intconst_t *nintconsts;
        Py_ssize_t i, n = PyTuple_GET_SIZE(globals) / 2;

        i = n * (sizeof(struct _cffi_global_s) + sizeof(cdl_intconst_t));
        building = PyMem_Malloc(i);
        if (building == NULL)
            goto error;
        memset(building, 0, i);
        nglobs = (struct _cffi_global_s *)building;
        nintconsts = (cdl_intconst_t *)(nglobs + n);

        for (i = 0; i < n; i++) {
            char *g = PyBytes_AS_STRING(PyTuple_GET_ITEM(globals, i * 2));
            nglobs[i].type_op = cdl_opcode(g); g += 4;
            nglobs[i].name = g;
            if (_CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_CONSTANT_INT ||
                _CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_ENUM) {
                PyObject *o = PyTuple_GET_ITEM(globals, i * 2 + 1);
                nglobs[i].address = &_cdl_realize_global_int;
#if PY_MAJOR_VERSION < 3
                if (PyInt_Check(o)) {
                    nintconsts[i].neg = PyInt_AS_LONG(o) <= 0;
                    nintconsts[i].value = (long long)PyInt_AS_LONG(o);
                }
                else
#endif
                {
                    nintconsts[i].neg = PyObject_RichCompareBool(o, Py_False,
                                                                 Py_LE);
                    nintconsts[i].value = PyLong_AsUnsignedLongLongMask(o);
                    if (PyErr_Occurred())
                        goto error;
                }
            }
        }
        ffi->types_builder.ctx.globals = nglobs;
        ffi->types_builder.ctx.num_globals = n;
        building = NULL;
    }

    if (struct_unions != NULL) {
        /* unpack a tuple of struct/unions, each described as a sub-tuple;
           the item 0 of each sub-tuple describes the struct/union, and
           the items 1..N-1 describe the fields, if any */
        struct _cffi_struct_union_s *nstructs;
        struct _cffi_field_s *nfields;
        Py_ssize_t i, n = PyTuple_GET_SIZE(struct_unions);
        Py_ssize_t nf = 0;   /* total number of fields */

        for (i = 0; i < n; i++) {
            nf += PyTuple_GET_SIZE(PyTuple_GET_ITEM(struct_unions, i)) - 1;
        }
        i = (n * sizeof(struct _cffi_struct_union_s) +
             nf * sizeof(struct _cffi_field_s));
        building = PyMem_Malloc(i);
        if (building == NULL)
            goto error;
        memset(building, 0, i);
        nstructs = (struct _cffi_struct_union_s *)building;
        nfields = (struct _cffi_field_s *)(nstructs + n);
        nf = 0;

        for (i = 0; i < n; i++) {
            /* 'desc' is the tuple of strings (desc_struct, desc_field_1, ..) */
            PyObject *desc = PyTuple_GET_ITEM(struct_unions, i);
            Py_ssize_t j, nf1 = PyTuple_GET_SIZE(desc) - 1;
            char *s = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, 0));
            /* 's' is the first string, describing the struct/union */
            nstructs[i].type_index = cdl_4bytes(s); s += 4;
            nstructs[i].flags = cdl_4bytes(s); s += 4;
            nstructs[i].name = s;
            if (nstructs[i].flags & (_CFFI_F_OPAQUE | _CFFI_F_EXTERNAL)) {
                nstructs[i].size = (size_t)-1;
                nstructs[i].alignment = -1;
                nstructs[i].first_field_index = -1;
                nstructs[i].num_fields = 0;
                assert(nf1 == 0);
            }
            else {
                nstructs[i].size = (size_t)-2;
                nstructs[i].alignment = -2;
                nstructs[i].first_field_index = nf;
                nstructs[i].num_fields = nf1;
            }
            for (j = 0; j < nf1; j++) {
                char *f = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, j + 1));
                /* 'f' is one of the other strings beyond the first one,
                   describing one field each */
                nfields[nf].field_type_op = cdl_opcode(f); f += 4;
                nfields[nf].field_offset = (size_t)-1;
                if (_CFFI_GETOP(nfields[nf].field_type_op) != _CFFI_OP_NOOP) {
                    nfields[nf].field_size = cdl_4bytes(f); f += 4;
                }
                else {
                    nfields[nf].field_size = (size_t)-1;
                }
                nfields[nf].name = f;
                nf++;
            }
        }
        ffi->types_builder.ctx.struct_unions = nstructs;
        ffi->types_builder.ctx.fields = nfields;
        ffi->types_builder.ctx.num_struct_unions = n;
        building = NULL;
    }

    if (enums != NULL) {
        /* unpack a tuple of strings, each of which describes one enum_s
           entry */
        struct _cffi_enum_s *nenums;
        Py_ssize_t i, n = PyTuple_GET_SIZE(enums);

        i = n * sizeof(struct _cffi_enum_s);
        building = PyMem_Malloc(i);
        if (building == NULL)
            goto error;
        memset(building, 0, i);
        nenums = (struct _cffi_enum_s *)building;

        for (i = 0; i < n; i++) {
            char *e = PyBytes_AS_STRING(PyTuple_GET_ITEM(enums, i));
            /* 'e' is a string describing the enum */
            nenums[i].type_index = cdl_4bytes(e); e += 4;
            nenums[i].type_prim = cdl_4bytes(e); e += 4;
            nenums[i].name = e; e += strlen(e) + 1;
            nenums[i].enumerators = e;
        }
        ffi->types_builder.ctx.enums = nenums;
        ffi->types_builder.ctx.num_enums = n;
        building = NULL;
    }

    if (typenames != NULL) {
        /* unpack a tuple of strings, each of which describes one typename_s
           entry */
        struct _cffi_typename_s *ntypenames;
        Py_ssize_t i, n = PyTuple_GET_SIZE(typenames);

        i = n * sizeof(struct _cffi_typename_s);
        building = PyMem_Malloc(i);
        if (building == NULL)
            goto error;
        memset(building, 0, i);
        ntypenames = (struct _cffi_typename_s *)building;

        for (i = 0; i < n; i++) {
            char *t = PyBytes_AS_STRING(PyTuple_GET_ITEM(typenames, i));
            /* 't' is a string describing the typename */
            ntypenames[i].type_index = cdl_4bytes(t); t += 4;
            ntypenames[i].name = t;
        }
        ffi->types_builder.ctx.typenames = ntypenames;
        ffi->types_builder.ctx.num_typenames = n;
        building = NULL;
    }

    if (includes != NULL) {
        PyObject *included_libs;

        included_libs = PyTuple_New(PyTuple_GET_SIZE(includes));
        if (included_libs == NULL)
            return -1;

        Py_INCREF(includes);
        ffi->types_builder.included_ffis = includes;
        ffi->types_builder.included_libs = included_libs;
    }

    /* Above, we took directly some "char *" strings out of the strings,
       typically from somewhere inside tuples.  Keep them alive by
       incref'ing the whole input arguments. */
    Py_INCREF(args);
    Py_XINCREF(kwds);
    ffi->types_builder._keepalive1 = args;
    ffi->types_builder._keepalive2 = kwds;
    return 0;

 error:
    if (building != NULL)
        PyMem_Free(building);
    if (!PyErr_Occurred())
        PyErr_NoMemory();
    return -1;
}
Beispiel #3
0
static int parse_sequel(token_t *tok, int outer)
{
    /* Emit opcodes for the "sequel", which is the optional part of a
       type declaration that follows the type name, i.e. everything
       with '*', '[ ]', '( )'.  Returns the entry point index pointing
       the innermost opcode (the one that corresponds to the complete
       type).  The 'outer' argument is the index of the opcode outside
       this "sequel".
     */
    int check_for_grouping, abi=0;
    _cffi_opcode_t result, *p_current;

 header:
    switch (tok->kind) {
    case TOK_STAR:
        outer = write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, outer));
        next_token(tok);
        goto header;
    case TOK_CONST:
        /* ignored for now */
        next_token(tok);
        goto header;
    case TOK_VOLATILE:
        /* ignored for now */
        next_token(tok);
        goto header;
    case TOK_CDECL:
    case TOK_STDCALL:
        /* must be in a function; checked below */
        abi = tok->kind;
        next_token(tok);
        goto header;
    default:
        break;
    }

    check_for_grouping = 1;
    if (tok->kind == TOK_IDENTIFIER) {
        next_token(tok);    /* skip a potential variable name */
        check_for_grouping = 0;
    }

    result = 0;
    p_current = &result;

    while (tok->kind == TOK_OPEN_PAREN) {
        next_token(tok);

        if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) {
            abi = tok->kind;
            next_token(tok);
        }

        if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR ||
                                            tok->kind == TOK_CONST ||
                                            tok->kind == TOK_VOLATILE ||
                                            tok->kind == TOK_OPEN_BRACKET)) {
            /* just parentheses for grouping.  Use a OP_NOOP to simplify */
            int x;
            assert(p_current == &result);
            x = tok->output_index;
            p_current = tok->output + x;

            write_ds(tok, _CFFI_OP(_CFFI_OP_NOOP, 0));

            x = parse_sequel(tok, x);
            result = _CFFI_OP(_CFFI_GETOP(0), x);
        }
        else {
            /* function type */
            int arg_total, base_index, arg_next, flags=0;

            if (abi == TOK_STDCALL) {
                flags = 2;
                /* note that an ellipsis below will overwrite this flags,
                   which is the goal: variadic functions are always cdecl */
            }
            abi = 0;

            if (tok->kind == TOK_VOID && get_following_char(tok) == ')') {
                next_token(tok);
            }

            /* (over-)estimate 'arg_total'.  May return 1 when it is really 0 */
            arg_total = number_of_commas(tok) + 1;

            *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
            p_current = tok->output + tok->output_index;

            base_index = write_ds(tok, _CFFI_OP(_CFFI_OP_FUNCTION, 0));
            if (base_index < 0)
                return -1;
            /* reserve (arg_total + 1) slots for the arguments and the
               final FUNCTION_END */
            for (arg_next = 0; arg_next <= arg_total; arg_next++)
                if (write_ds(tok, _CFFI_OP(0, 0)) < 0)
                    return -1;

            arg_next = base_index + 1;

            if (tok->kind != TOK_CLOSE_PAREN) {
                while (1) {
                    int arg;
                    _cffi_opcode_t oarg;

                    if (tok->kind == TOK_DOTDOTDOT) {
                        flags = 1;   /* ellipsis */
                        next_token(tok);
                        break;
                    }
                    arg = parse_complete(tok);
                    switch (_CFFI_GETOP(tok->output[arg])) {
                    case _CFFI_OP_ARRAY:
                    case _CFFI_OP_OPEN_ARRAY:
                        arg = _CFFI_GETARG(tok->output[arg]);
                        /* fall-through */
                    case _CFFI_OP_FUNCTION:
                        oarg = _CFFI_OP(_CFFI_OP_POINTER, arg);
                        break;
                    default:
                        oarg = _CFFI_OP(_CFFI_OP_NOOP, arg);
                        break;
                    }
                    assert(arg_next - base_index <= arg_total);
                    tok->output[arg_next++] = oarg;
                    if (tok->kind != TOK_COMMA)
                        break;
                    next_token(tok);
                }
            }
            tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags);
        }

        if (tok->kind != TOK_CLOSE_PAREN)
            return parse_error(tok, "expected ')'");
        next_token(tok);
    }

    if (abi != 0)
        return parse_error(tok, "expected '('");

    while (tok->kind == TOK_OPEN_BRACKET) {
        *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
        p_current = tok->output + tok->output_index;

        next_token(tok);
        if (tok->kind != TOK_CLOSE_BRACKET) {
            size_t length;
            int gindex;
            char *endptr;

            switch (tok->kind) {

            case TOK_INTEGER:
                errno = 0;
                if (sizeof(length) > sizeof(unsigned long)) {
#ifdef MS_WIN32
# ifdef _WIN64
                    length = _strtoui64(tok->p, &endptr, 0);
# else
                    abort();  /* unreachable */
# endif
#else
                    length = strtoull(tok->p, &endptr, 0);
#endif
                }
                else
                    length = strtoul(tok->p, &endptr, 0);
                if (endptr != tok->p + tok->size)
                    return parse_error(tok, "invalid number");
                if (errno == ERANGE || length > MAX_SSIZE_T)
                    return parse_error(tok, "number too large");
                break;

            case TOK_IDENTIFIER:
                gindex = search_in_globals(tok->info->ctx, tok->p, tok->size);
                if (gindex >= 0) {
                    const struct _cffi_global_s *g;
                    g = &tok->info->ctx->globals[gindex];
                    if (_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT_INT ||
                        _CFFI_GETOP(g->type_op) == _CFFI_OP_ENUM) {
                        int neg;
                        struct _cffi_getconst_s gc;
                        gc.ctx = tok->info->ctx;
                        gc.gindex = gindex;
                        neg = ((int(*)(struct _cffi_getconst_s*))g->address)
                            (&gc);
                        if (neg == 0 && gc.value > MAX_SSIZE_T)
                            return parse_error(tok,
                                               "integer constant too large");
                        if (neg == 0 || gc.value == 0) {
                            length = (size_t)gc.value;
                            break;
                        }
                        if (neg != 1)
                            return parse_error(tok, "disagreement about"
                                               " this constant's value");
                    }
                }
                /* fall-through to the default case */
            default:
                return parse_error(tok, "expected a positive integer constant");
            }

            next_token(tok);

            write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0));
            write_ds(tok, (_cffi_opcode_t)length);
        }
        else
            write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, 0));

        if (tok->kind != TOK_CLOSE_BRACKET)
            return parse_error(tok, "expected ']'");
        next_token(tok);
    }

    *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), outer);
    return _CFFI_GETARG(result);
}