summaryrefslogtreecommitdiffstats
path: root/Python/getargs.c
diff options
context:
space:
mode:
authorJean-Paul Calderone <exarkun@twistedmatrix.com>2012-03-16 12:51:42 (GMT)
committerJean-Paul Calderone <exarkun@twistedmatrix.com>2012-03-16 12:51:42 (GMT)
commitc961b4abaa27109c2643abd2af9eaaf541ae7696 (patch)
treecfde952244754beed8be422b48c18e5a0eb9ec24 /Python/getargs.c
parent68ca5a6bcc5712eaebbdf40a7ff3ea49f8654c34 (diff)
downloadcpython-c961b4abaa27109c2643abd2af9eaaf541ae7696.zip
cpython-c961b4abaa27109c2643abd2af9eaaf541ae7696.tar.gz
cpython-c961b4abaa27109c2643abd2af9eaaf541ae7696.tar.bz2
Issue #14325: Stop using python lists, capsules, and the garbage collector to deal with PyArg_Parse* cleanup.
Diffstat (limited to 'Python/getargs.c')
-rw-r--r--Python/getargs.c205
1 files changed, 85 insertions, 120 deletions
diff --git a/Python/getargs.c b/Python/getargs.c
index 77f27be..37f1898 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -33,16 +33,33 @@ PyAPI_FUNC(int) _PyArg_VaParseTupleAndKeywords_SizeT(PyObject *, PyObject *,
#define FLAG_COMPAT 1
#define FLAG_SIZE_T 2
+typedef int (*destr_t)(PyObject *, void *);
+
+
+/* Keep track of "objects" that have been allocated or initialized and
+ which will need to be deallocated or cleaned up somehow if overall
+ parsing fails.
+*/
+typedef struct {
+ void *item;
+ destr_t destructor;
+} freelistentry_t;
+
+typedef struct {
+ int first_available;
+ freelistentry_t *entries;
+} freelist_t;
+
/* Forward */
static int vgetargs1(PyObject *, const char *, va_list *, int);
static void seterror(int, const char *, int *, const char *, const char *);
static char *convertitem(PyObject *, const char **, va_list *, int, int *,
- char *, size_t, PyObject **);
+ char *, size_t, freelist_t *);
static char *converttuple(PyObject *, const char **, va_list *, int,
- int *, char *, size_t, int, PyObject **);
+ int *, char *, size_t, int, freelist_t *);
static char *convertsimple(PyObject *, const char **, va_list *, int, char *,
- size_t, PyObject **);
+ size_t, freelist_t *);
static Py_ssize_t convertbuffer(PyObject *, void **p, char **);
static int getbuffer(PyObject *, Py_buffer *, char**);
@@ -127,111 +144,54 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va)
#define GETARGS_CAPSULE_NAME_CLEANUP_BUFFER "getargs.cleanup_buffer"
#define GETARGS_CAPSULE_NAME_CLEANUP_CONVERT "getargs.cleanup_convert"
-static void
-cleanup_ptr(PyObject *self)
+static int
+cleanup_ptr(PyObject *self, void *ptr)
{
- void *ptr = PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_PTR);
if (ptr) {
PyMem_FREE(ptr);
}
+ return 0;
}
-static void
-cleanup_buffer(PyObject *self)
+static int
+cleanup_buffer(PyObject *self, void *ptr)
{
- Py_buffer *ptr = (Py_buffer *)PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_BUFFER);
- if (ptr) {
- PyBuffer_Release(ptr);
+ Py_buffer *buf = (Py_buffer *)ptr;
+ if (buf) {
+ PyBuffer_Release(buf);
}
+ return 0;
}
static int
-addcleanup(void *ptr, PyObject **freelist, int is_buffer)
+addcleanup(void *ptr, freelist_t *freelist, destr_t destructor)
{
- PyObject *cobj;
- const char *name;
- PyCapsule_Destructor destr;
-
- if (is_buffer) {
- destr = cleanup_buffer;
- name = GETARGS_CAPSULE_NAME_CLEANUP_BUFFER;
- } else {
- destr = cleanup_ptr;
- name = GETARGS_CAPSULE_NAME_CLEANUP_PTR;
- }
-
- if (!*freelist) {
- *freelist = PyList_New(0);
- if (!*freelist) {
- destr(ptr);
- return -1;
- }
- }
+ int index;
- cobj = PyCapsule_New(ptr, name, destr);
- if (!cobj) {
- destr(ptr);
- return -1;
- }
- if (PyList_Append(*freelist, cobj)) {
- Py_DECREF(cobj);
- return -1;
- }
- Py_DECREF(cobj);
- return 0;
-}
+ index = freelist->first_available;
+ freelist->first_available += 1;
-static void
-cleanup_convert(PyObject *self)
-{
- typedef int (*destr_t)(PyObject *, void *);
- destr_t destr = (destr_t)PyCapsule_GetContext(self);
- void *ptr = PyCapsule_GetPointer(self,
- GETARGS_CAPSULE_NAME_CLEANUP_CONVERT);
- if (ptr && destr)
- destr(NULL, ptr);
-}
+ freelist->entries[index].item = ptr;
+ freelist->entries[index].destructor = destructor;
-static int
-addcleanup_convert(void *ptr, PyObject **freelist, int (*destr)(PyObject*,void*))
-{
- PyObject *cobj;
- if (!*freelist) {
- *freelist = PyList_New(0);
- if (!*freelist) {
- destr(NULL, ptr);
- return -1;
- }
- }
- cobj = PyCapsule_New(ptr, GETARGS_CAPSULE_NAME_CLEANUP_CONVERT,
- cleanup_convert);
- if (!cobj) {
- destr(NULL, ptr);
- return -1;
- }
- if (PyCapsule_SetContext(cobj, destr) == -1) {
- /* This really should not happen. */
- Py_FatalError("capsule refused setting of context.");
- }
- if (PyList_Append(*freelist, cobj)) {
- Py_DECREF(cobj); /* This will also call destr. */
- return -1;
- }
- Py_DECREF(cobj);
return 0;
}
static int
-cleanreturn(int retval, PyObject *freelist)
+cleanreturn(int retval, freelist_t *freelist)
{
- if (freelist && retval != 0) {
- /* We were successful, reset the destructors so that they
- don't get called. */
- Py_ssize_t len = PyList_GET_SIZE(freelist), i;
- for (i = 0; i < len; i++)
- PyCapsule_SetDestructor(PyList_GET_ITEM(freelist, i), NULL);
- }
- Py_XDECREF(freelist);
+ int index;
+
+ if (retval == 0) {
+ /* A failure occurred, therefore execute all of the cleanup
+ functions.
+ */
+ for (index = 0; index < freelist->first_available; ++index) {
+ freelist->entries[index].destructor(NULL,
+ freelist->entries[index].item);
+ }
+ }
+ PyMem_Free(freelist->entries);
return retval;
}
@@ -250,7 +210,7 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
const char *formatsave = format;
Py_ssize_t i, len;
char *msg;
- PyObject *freelist = NULL;
+ freelist_t freelist = {0, NULL};
int compat = flags & FLAG_COMPAT;
assert(compat || (args != (PyObject*)NULL));
@@ -306,6 +266,8 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
format = formatsave;
+ freelist.entries = PyMem_New(freelistentry_t, max);
+
if (compat) {
if (max == 0) {
if (args == NULL)
@@ -314,7 +276,7 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
"%.200s%s takes no arguments",
fname==NULL ? "function" : fname,
fname==NULL ? "" : "()");
- return 0;
+ return cleanreturn(0, &freelist);
}
else if (min == 1 && max == 1) {
if (args == NULL) {
@@ -322,26 +284,26 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
"%.200s%s takes at least one argument",
fname==NULL ? "function" : fname,
fname==NULL ? "" : "()");
- return 0;
+ return cleanreturn(0, &freelist);
}
msg = convertitem(args, &format, p_va, flags, levels,
msgbuf, sizeof(msgbuf), &freelist);
if (msg == NULL)
- return cleanreturn(1, freelist);
+ return cleanreturn(1, &freelist);
seterror(levels[0], msg, levels+1, fname, message);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
else {
PyErr_SetString(PyExc_SystemError,
"old style getargs format uses new features");
- return 0;
+ return cleanreturn(0, &freelist);
}
}
if (!PyTuple_Check(args)) {
PyErr_SetString(PyExc_SystemError,
"new style getargs format but argument is not a tuple");
- return 0;
+ return cleanreturn(0, &freelist);
}
len = PyTuple_GET_SIZE(args);
@@ -359,7 +321,7 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
Py_SAFE_DOWNCAST(len, Py_ssize_t, long));
else
PyErr_SetString(PyExc_TypeError, message);
- return 0;
+ return cleanreturn(0, &freelist);
}
for (i = 0; i < len; i++) {
@@ -370,7 +332,7 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
sizeof(msgbuf), &freelist);
if (msg) {
seterror(i+1, msg, levels, fname, msg);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
}
@@ -379,10 +341,10 @@ vgetargs1(PyObject *args, const char *format, va_list *p_va, int flags)
*format != '|' && *format != ':' && *format != ';') {
PyErr_Format(PyExc_SystemError,
"bad format string: %.200s", formatsave);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
- return cleanreturn(1, freelist);
+ return cleanreturn(1, &freelist);
}
@@ -446,7 +408,7 @@ seterror(int iarg, const char *msg, int *levels, const char *fname,
static char *
converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
int *levels, char *msgbuf, size_t bufsize, int toplevel,
- PyObject **freelist)
+ freelist_t *freelist)
{
int level = 0;
int n = 0;
@@ -521,7 +483,7 @@ converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
static char *
convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags,
- int *levels, char *msgbuf, size_t bufsize, PyObject **freelist)
+ int *levels, char *msgbuf, size_t bufsize, freelist_t *freelist)
{
char *msg;
const char *format = *p_format;
@@ -586,7 +548,7 @@ float_argument_error(PyObject *arg)
static char *
convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
- char *msgbuf, size_t bufsize, PyObject **freelist)
+ char *msgbuf, size_t bufsize, freelist_t *freelist)
{
/* For # codes */
#define FETCH_SIZE int *q=NULL;Py_ssize_t *q2=NULL;\
@@ -863,7 +825,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (getbuffer(arg, (Py_buffer*)p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize);
format++;
- if (addcleanup(p, freelist, 1)) {
+ if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
@@ -908,7 +870,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (getbuffer(arg, p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize);
}
- if (addcleanup(p, freelist, 1)) {
+ if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
@@ -1120,7 +1082,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
PyErr_NoMemory();
RETURN_ERR_OCCURRED;
}
- if (addcleanup(*buffer, freelist, 0)) {
+ if (addcleanup(*buffer, freelist, cleanup_ptr)) {
Py_DECREF(s);
return converterr(
"(cleanup problem)",
@@ -1162,7 +1124,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
PyErr_NoMemory();
RETURN_ERR_OCCURRED;
}
- if (addcleanup(*buffer, freelist, 0)) {
+ if (addcleanup(*buffer, freelist, cleanup_ptr)) {
Py_DECREF(s);
return converterr("(cleanup problem)",
arg, msgbuf, bufsize);
@@ -1223,7 +1185,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
return converterr("(unspecified)",
arg, msgbuf, bufsize);
if (res == Py_CLEANUP_SUPPORTED &&
- addcleanup_convert(addr, freelist, convert) == -1)
+ addcleanup(addr, freelist, convert) == -1)
return converterr("(cleanup problem)",
arg, msgbuf, bufsize);
}
@@ -1254,7 +1216,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
PyBuffer_Release((Py_buffer*)p);
return converterr("contiguous buffer", arg, msgbuf, bufsize);
}
- if (addcleanup(p, freelist, 1)) {
+ if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
@@ -1442,7 +1404,8 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
const char *fname, *msg, *custom_msg, *keyword;
int min = INT_MAX;
int i, len, nargs, nkeywords;
- PyObject *freelist = NULL, *current_arg;
+ PyObject *current_arg;
+ freelist_t freelist = {0, NULL};
assert(args != NULL && PyTuple_Check(args));
assert(keywords == NULL || PyDict_Check(keywords));
@@ -1466,6 +1429,8 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
for (len=0; kwlist[len]; len++)
continue;
+ freelist.entries = PyMem_New(freelistentry_t, len);
+
nargs = PyTuple_GET_SIZE(args);
nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords);
if (nargs + nkeywords > len) {
@@ -1490,7 +1455,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
PyErr_Format(PyExc_RuntimeError,
"More keyword list entries (%d) than "
"format specifiers (%d)", len, i);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
current_arg = NULL;
if (nkeywords) {
@@ -1504,11 +1469,11 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
"Argument given by name ('%s') "
"and position (%d)",
keyword, i+1);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
}
else if (nkeywords && PyErr_Occurred())
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
else if (i < nargs)
current_arg = PyTuple_GET_ITEM(args, i);
@@ -1517,7 +1482,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
levels, msgbuf, sizeof(msgbuf), &freelist);
if (msg) {
seterror(i+1, msg, levels, fname, custom_msg);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
continue;
}
@@ -1526,14 +1491,14 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
PyErr_Format(PyExc_TypeError, "Required argument "
"'%s' (pos %d) not found",
keyword, i+1);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
/* current code reports success when all required args
* fulfilled and no keyword args left, with no further
* validation. XXX Maybe skip this in debug build ?
*/
if (!nkeywords)
- return cleanreturn(1, freelist);
+ return cleanreturn(1, &freelist);
/* We are into optional args, skip thru to any remaining
* keyword args */
@@ -1541,7 +1506,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
if (msg) {
PyErr_Format(PyExc_RuntimeError, "%s: '%s'", msg,
format);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
}
@@ -1549,7 +1514,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
PyErr_Format(PyExc_RuntimeError,
"more argument specifiers than keyword list entries "
"(remaining format:'%s')", format);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
/* make sure there are no extraneous keyword arguments */
@@ -1562,7 +1527,7 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
if (!PyUnicode_Check(key)) {
PyErr_SetString(PyExc_TypeError,
"keywords must be strings");
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
/* check that _PyUnicode_AsString() result is not NULL */
ks = _PyUnicode_AsString(key);
@@ -1579,12 +1544,12 @@ vgetargskeywords(PyObject *args, PyObject *keywords, const char *format,
"'%U' is an invalid keyword "
"argument for this function",
key);
- return cleanreturn(0, freelist);
+ return cleanreturn(0, &freelist);
}
}
}
- return cleanreturn(1, freelist);
+ return cleanreturn(1, &freelist);
}