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" */ }
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; }
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); }