diff -r 52597f888e7a Python/getargs.c --- a/Python/getargs.c Thu Mar 15 18:08:34 2012 +0100 +++ b/Python/getargs.c Thu Mar 15 15:07:04 2012 -0400 @@ -33,16 +33,35 @@ #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 _Py_CleanupFreelistEntry +{ + void *item; + destr_t destructor; +} freelistentry_t; + +typedef struct _Py_CleanupFreelist +{ + 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 +146,56 @@ #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); } -} - -static void -cleanup_buffer(PyObject *self) -{ - Py_buffer *ptr = (Py_buffer *)PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_BUFFER); - if (ptr) { - PyBuffer_Release(ptr); - } -} - -static int -addcleanup(void *ptr, PyObject **freelist, int is_buffer) -{ - 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; - } - } - - 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; -} - -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); -} - -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) +cleanup_buffer(PyObject *self, void *ptr) { - 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_buffer *buf = (Py_buffer *)ptr; + if (buf) { + PyBuffer_Release(buf); } - Py_XDECREF(freelist); + return 0; +} + +static int +addcleanup(void *ptr, freelist_t *freelist, destr_t destructor) +{ + int index; + + index = freelist->first_available; + freelist->first_available += 1; + + freelist->entries[index].item = ptr; + freelist->entries[index].destructor = destructor; + + return 0; +} + +static int +cleanreturn(int retval, freelist_t *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); + PyMem_Free(freelist); return retval; } @@ -250,7 +214,7 @@ const char *formatsave = format; Py_ssize_t i, len; char *msg; - PyObject *freelist = NULL; + freelist_t *freelist; int compat = flags & FLAG_COMPAT; assert(compat || (args != (PyObject*)NULL)); @@ -306,6 +270,10 @@ format = formatsave; + freelist = PyMem_New(freelist_t, 1); + freelist->first_available = 0; + freelist->entries = PyMem_New(freelistentry_t, max); + if (compat) { if (max == 0) { if (args == NULL) @@ -314,7 +282,7 @@ "%.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,10 +290,10 @@ "%.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); + msgbuf, sizeof(msgbuf), freelist); if (msg == NULL) return cleanreturn(1, freelist); seterror(levels[0], msg, levels+1, fname, message); @@ -334,14 +302,14 @@ 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 +327,7 @@ 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++) { @@ -367,7 +335,7 @@ format++; msg = convertitem(PyTuple_GET_ITEM(args, i), &format, p_va, flags, levels, msgbuf, - sizeof(msgbuf), &freelist); + sizeof(msgbuf), freelist); if (msg) { seterror(i+1, msg, levels, fname, msg); return cleanreturn(0, freelist); @@ -446,7 +414,7 @@ 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 +489,7 @@ 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 +554,7 @@ 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 +831,7 @@ 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 +876,7 @@ 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 +1088,7 @@ 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 +1130,7 @@ 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 +1191,7 @@ 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 +1222,7 @@ 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 +1410,8 @@ 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 = NULL; assert(args != NULL && PyTuple_Check(args)); assert(keywords == NULL || PyDict_Check(keywords)); @@ -1466,6 +1435,10 @@ for (len=0; kwlist[len]; len++) continue; + freelist = PyMem_New(freelist_t, 1); + freelist->first_available = 0; + freelist->entries = PyMem_New(freelistentry_t, len); + nargs = PyTuple_GET_SIZE(args); nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords); if (nargs + nkeywords > len) { @@ -1514,7 +1487,7 @@ if (current_arg) { msg = convertitem(current_arg, &format, p_va, flags, - levels, msgbuf, sizeof(msgbuf), &freelist); + levels, msgbuf, sizeof(msgbuf), freelist); if (msg) { seterror(i+1, msg, levels, fname, custom_msg); return cleanreturn(0, freelist);