ext/Python/getargs.cpp (2,084 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include <cassert>
#include <climits>
#include <cstdarg>
#include "cpython-data.h"
#include "cpython-func.h"
#include "runtime.h"
namespace py {
#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 {
freelistentry_t* entries;
int first_available;
int entries_malloced;
} freelist_t;
#define STATIC_FREELIST_ENTRIES 8
static const int kMaxSmallArraySize = 16;
// Forward
static int vGetArgs1Impl(PyObject* args, PyObject* const* stack,
Py_ssize_t nargs, const char* format, va_list* p_va,
int flags);
static int vgetargs1(PyObject*, const char*, va_list*, int);
static void seterror(Py_ssize_t, const char*, int*, const char*, const char*);
static const char* convertitem(PyObject*, const char**, va_list*, int, int*,
char*, size_t, freelist_t*);
static const char* converttuple(PyObject*, const char**, va_list*, int, int*,
char*, size_t, int, freelist_t*);
static const char* convertsimple(PyObject*, const char**, va_list*, int, char*,
size_t, freelist_t*);
static Py_ssize_t convertbuffer(PyObject*, void const** p, const char**);
static int getbuffer(PyObject*, Py_buffer*, const char**);
static int vgetargskeywords(PyObject*, PyObject*, const char*, char**, va_list*,
int);
static int vGetArgsKeywordsFast(PyObject*, PyObject*, struct _PyArg_Parser*,
va_list*, int);
static int vGetArgsKeywordsFastImpl(PyObject* const* args, Py_ssize_t nargs,
PyObject* keywords, PyObject* kwnames,
struct _PyArg_Parser* parser, va_list* p_va,
int flags);
static bool parserInit(struct _PyArg_Parser* parser, int* keyword_count);
static PyObject* findKeyword(PyObject* kwnames, PyObject* const* kwstack,
const char* key);
static const char* skipitem(const char**, va_list*, int);
PY_EXPORT int PyArg_Parse(PyObject* args, const char* format, ...) {
int retval;
va_list va;
va_start(va, format);
retval = vgetargs1(args, format, &va, FLAG_COMPAT);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_Parse_SizeT(PyObject* args, const char* format, ...) {
int retval;
va_list va;
va_start(va, format);
retval = vgetargs1(args, format, &va, FLAG_COMPAT | FLAG_SIZE_T);
va_end(va);
return retval;
}
PY_EXPORT int PyArg_ParseTuple(PyObject* args, const char* format, ...) {
int retval;
va_list va;
va_start(va, format);
retval = vgetargs1(args, format, &va, 0);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseTuple_SizeT(PyObject* args, const char* format, ...) {
int retval;
va_list va;
va_start(va, format);
retval = vgetargs1(args, format, &va, FLAG_SIZE_T);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseStack(PyObject* const* args, Py_ssize_t nargs,
const char* format, ...) {
va_list va;
va_start(va, format);
int retval = vGetArgs1Impl(nullptr, args, nargs, format, &va, 0);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseStack_SizeT(PyObject* const* args, Py_ssize_t nargs,
const char* format, ...) {
va_list va;
va_start(va, format);
int retval = vGetArgs1Impl(nullptr, args, nargs, format, &va, FLAG_SIZE_T);
va_end(va);
return retval;
}
PY_EXPORT int PyArg_VaParse(PyObject* args, const char* format, va_list va) {
va_list lva;
int retval;
va_copy(lva, va);
retval = vgetargs1(args, format, &lva, 0);
va_end(lva);
return retval;
}
PY_EXPORT int _PyArg_VaParse_SizeT(PyObject* args, const char* format,
va_list va) {
va_list lva;
int retval;
va_copy(lva, va);
retval = vgetargs1(args, format, &lva, FLAG_SIZE_T);
va_end(lva);
return retval;
}
// Handle cleanup of allocated memory in case of exception
static int cleanup_ptr(PyObject*, void* ptr) {
if (ptr) {
PyMem_FREE(ptr);
}
return 0;
}
static int cleanup_buffer(PyObject*, void* ptr) {
Py_buffer* buf = static_cast<Py_buffer*>(ptr);
if (buf) {
PyBuffer_Release(buf);
}
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(nullptr,
freelist->entries[index].item);
}
}
if (freelist->entries_malloced) PyMem_FREE(freelist->entries);
return retval;
}
static int vGetArgs1Impl(PyObject* compat_args, PyObject* const* stack,
Py_ssize_t nargs, const char* format, va_list* p_va,
int flags) {
DCHECK(nargs == 0 || stack != nullptr,
"if nargs == 0, stack must be nullptr");
int compat = flags & FLAG_COMPAT;
flags = flags & ~FLAG_COMPAT;
int endfmt = 0;
const char* formatsave = format;
const char* fname = nullptr;
int level = 0;
int max = 0;
const char* message = nullptr;
int min = -1;
while (endfmt == 0) {
int c = *format++;
switch (c) {
case '(':
if (level == 0) max++;
level++;
if (level >= 30) {
Py_FatalError(
"too many tuple nesting levels "
"in argument format string");
}
break;
case ')':
if (level == 0) {
Py_FatalError("excess ')' in getargs format");
} else {
level--;
}
break;
case '\0':
endfmt = 1;
break;
case ':':
fname = format;
endfmt = 1;
break;
case ';':
message = format;
endfmt = 1;
break;
case '|':
if (level == 0) min = max;
break;
default:
if (level == 0 && Py_ISALPHA(Py_CHARMASK(c)) && c != 'e') {
/* skip encoded */
max++;
}
break;
}
}
if (level != 0) Py_FatalError(/* '(' */ "missing ')' in getargs format");
if (min < 0) min = max;
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
freelist_t freelist;
freelist.entries = static_entries;
freelist.first_available = 0;
freelist.entries_malloced = 0;
if (max > STATIC_FREELIST_ENTRIES) {
freelist.entries = PyMem_NEW(freelistentry_t, max);
if (freelist.entries == nullptr) {
PyErr_NoMemory();
return 0;
}
freelist.entries_malloced = 1;
}
format = formatsave;
int levels[32];
const char* msg;
char msgbuf[256];
if (compat) {
if (max == 0) {
if (compat_args == nullptr) return 1;
PyErr_Format(PyExc_TypeError, "%.200s%s takes no arguments",
fname == nullptr ? "function" : fname,
fname == nullptr ? "" : "()");
return cleanreturn(0, &freelist);
}
if (min == 1 && max == 1) {
if (compat_args == nullptr) {
PyErr_Format(PyExc_TypeError, "%.200s%s takes at least one argument",
fname == nullptr ? "function" : fname,
fname == nullptr ? "" : "()");
return cleanreturn(0, &freelist);
}
msg = convertitem(compat_args, &format, p_va, flags, levels, msgbuf,
sizeof(msgbuf), &freelist);
if (msg == nullptr) return cleanreturn(1, &freelist);
seterror(levels[0], msg, levels + 1, fname, message);
return cleanreturn(0, &freelist);
}
Thread::current()->raiseWithFmt(
LayoutId::kSystemError, "old style getargs format uses new features");
return cleanreturn(0, &freelist);
}
if (nargs < min || max < nargs) {
if (message == nullptr) {
PyErr_Format(
PyExc_TypeError, "%.150s%s takes %s %d argument%s (%zd given)",
fname == nullptr ? "function" : fname, fname == nullptr ? "" : "()",
min == max ? "exactly"
: nargs < min ? "at least"
: "at most",
nargs < min ? min : max, (nargs < min ? min : max) == 1 ? "" : "s",
nargs);
} else {
Thread::current()->raiseWithFmt(LayoutId::kTypeError, message);
}
return cleanreturn(0, &freelist);
}
for (Py_ssize_t i = 0; i < nargs; i++) {
if (*format == '|') format++;
msg = convertitem(stack[i], &format, p_va, flags, levels, msgbuf,
sizeof(msgbuf), &freelist);
if (msg) {
seterror(i + 1, msg, levels, fname, message);
return cleanreturn(0, &freelist);
}
}
if (*format != '\0' && !Py_ISALPHA(Py_CHARMASK(*format)) && *format != '(' &&
*format != '|' && *format != ':' && *format != ';') {
PyErr_Format(PyExc_SystemError, "bad format string: %.200s", formatsave);
return cleanreturn(0, &freelist);
}
return cleanreturn(1, &freelist);
}
static int vgetargs1(PyObject* args, const char* format, va_list* p_va,
int flags) {
if (flags & FLAG_COMPAT) {
return vGetArgs1Impl(args, nullptr, 0, format, p_va, flags);
}
DCHECK(args != nullptr, "args must be non-NULL");
if (!PyTuple_Check(args)) {
Thread::current()->raiseWithFmt(
LayoutId::kSystemError,
"new style getargs format but argument is not a tuple");
return 0;
}
PyObject* small_array[kMaxSmallArraySize];
Py_ssize_t nargs = PyTuple_Size(args);
PyObject** array =
nargs <= kMaxSmallArraySize ? small_array : new PyObject*[nargs];
for (Py_ssize_t i = 0; i < nargs; i++) {
array[i] = PyTuple_GET_ITEM(args, i);
}
int retval = vGetArgs1Impl(args, array, nargs, format, p_va, flags);
if (array != small_array) {
delete[] array;
}
return retval;
}
static void seterror(Py_ssize_t iarg, const char* msg, int* levels,
const char* fname, const char* message) {
char buf[512];
int i;
char* p = buf;
if (PyErr_Occurred()) return;
if (message == nullptr) {
if (fname != nullptr) {
PyOS_snprintf(p, sizeof(buf), "%.200s() ", fname);
p += std::strlen(p);
}
if (iarg != 0) {
PyOS_snprintf(p, sizeof(buf) - (p - buf),
"argument %" PY_FORMAT_SIZE_T "d", iarg);
i = 0;
p += std::strlen(p);
while (i < 32 && levels[i] > 0 && static_cast<int>(p - buf) < 220) {
PyOS_snprintf(p, sizeof(buf) - (p - buf), ", item %d", levels[i] - 1);
p += std::strlen(p);
i++;
}
} else {
PyOS_snprintf(p, sizeof(buf) - (p - buf), "argument");
p += std::strlen(p);
}
PyOS_snprintf(p, sizeof(buf) - (p - buf), " %.256s", msg);
message = buf;
}
if (msg[0] == '(') {
Thread::current()->raiseWithFmt(LayoutId::kSystemError, message);
} else {
Thread::current()->raiseWithFmt(LayoutId::kTypeError, message);
}
}
// Convert a tuple argument.
// On entry, *p_format points to the character _after_ the opening '('.
// On successful exit, *p_format points to the closing ')'.
// If successful:
// *p_format and *p_va are updated,
// *levels and *msgbuf are untouched,
// and nullptr is returned.
// If the argument is invalid:
// *p_format is unchanged,
// *p_va is undefined,
// *levels is a 0-terminated list of item numbers,
// *msgbuf contains an error message, whose format is:
// "must be <typename1>, not <typename2>", where:
// <typename1> is the name of the expected type, and
// <typename2> is the name of the actual type,
// and msgbuf is returned.
static const char* converttuple(PyObject* arg, const char** p_format,
va_list* p_va, int flags, int* levels,
char* msgbuf, size_t bufsize, int toplevel,
freelist_t* freelist) {
int level = 0;
int n = 0;
const char* format = *p_format;
int i;
Py_ssize_t len;
for (;;) {
int c = *format++;
if (c == '(') {
if (level == 0) n++;
level++;
} else if (c == ')') {
if (level == 0) break;
level--;
} else if (c == ':' || c == ';' || c == '\0') {
break;
} else if (level == 0 && Py_ISALPHA(Py_CHARMASK(c))) {
n++;
}
}
if (!PySequence_Check(arg) || PyBytes_Check(arg)) {
levels[0] = 0;
PyOS_snprintf(msgbuf, bufsize,
toplevel ? "expected %d arguments, not %.50s"
: "must be %d-item sequence, not %.50s",
n, arg == Py_None ? "None" : _PyType_Name(Py_TYPE(arg)));
return msgbuf;
}
len = PySequence_Size(arg);
if (len != n) {
levels[0] = 0;
if (toplevel) {
PyOS_snprintf(msgbuf, bufsize,
"expected %d arguments, not %" PY_FORMAT_SIZE_T "d", n,
len);
} else {
PyOS_snprintf(msgbuf, bufsize,
"must be sequence of length %d, "
"not %" PY_FORMAT_SIZE_T "d",
n, len);
}
return msgbuf;
}
format = *p_format;
for (i = 0; i < n; i++) {
const char* msg;
PyObject* item;
item = PySequence_GetItem(arg, i);
if (item == nullptr) {
PyErr_Clear();
levels[0] = i + 1;
levels[1] = 0;
strncpy(msgbuf, "is not retrievable", bufsize);
return msgbuf;
}
msg = convertitem(item, &format, p_va, flags, levels + 1, msgbuf, bufsize,
freelist);
// PySequence_GetItem calls tp->sq_item, which INCREFs
Py_XDECREF(item);
if (msg != nullptr) {
levels[0] = i + 1;
return msg;
}
}
*p_format = format;
return nullptr;
}
// Convert a single item.
static const char* convertitem(PyObject* arg, const char** p_format,
va_list* p_va, int flags, int* levels,
char* msgbuf, size_t bufsize,
freelist_t* freelist) {
const char* msg;
const char* format = *p_format;
if (*format == '(') {
format++;
msg = converttuple(arg, &format, p_va, flags, levels, msgbuf, bufsize, 0,
freelist);
if (msg == nullptr) format++;
} else {
msg = convertsimple(arg, &format, p_va, flags, msgbuf, bufsize, freelist);
if (msg != nullptr) levels[0] = 0;
}
if (msg == nullptr) *p_format = format;
return msg;
}
// Format an error message generated by convertsimple().
static const char* converterr(const char* expected, PyObject* arg, char* msgbuf,
size_t bufsize) {
assert(expected != nullptr);
assert(arg != nullptr);
if (expected[0] == '(') {
PyOS_snprintf(msgbuf, bufsize, "%.100s", expected);
} else {
PyOS_snprintf(msgbuf, bufsize, "must be %.50s, not %.50s", expected,
arg == Py_None ? "None" : _PyType_Name(Py_TYPE(arg)));
}
return msgbuf;
}
#define CONV_UNICODE "(unicode conversion error)"
// Explicitly check for float arguments when integers are expected.
// Return 1 for error, 0 if ok.
static int float_argument_error(PyObject* arg) {
if (PyFloat_Check(arg)) {
Thread::current()->raiseWithFmt(LayoutId::kTypeError,
"integer argument expected, got float");
return 1;
}
return 0;
}
// Convert a non-tuple argument. Return nullptr if conversion went OK,
// or a string with a message describing the failure. The message is
// formatted as "must be <desired type>, not <actual type>".
// When failing, an exception may or may not have been raised.
// Don't call if a tuple is expected.
//
// When you add new format codes, please don't forget poor skipitem() below.
static const char* convertsimple(PyObject* arg, const char** p_format,
va_list* p_va, int flags, char* msgbuf,
size_t bufsize, freelist_t* freelist) {
// For # codes
#define FETCH_SIZE \
int* q = nullptr; \
Py_ssize_t* q2 = nullptr; \
if (flags & FLAG_SIZE_T) \
q2 = va_arg(*p_va, Py_ssize_t*); \
else \
q = va_arg(*p_va, int*);
#define STORE_SIZE(s) \
if (flags & FLAG_SIZE_T) \
*q2 = s; \
else { \
if (INT_MAX < s) { \
Thread::current()->raiseWithFmt(LayoutId::kOverflowError, \
"size does not fit in an int"); \
return converterr("", arg, msgbuf, bufsize); \
} \
*q = (int)s; \
}
#define BUFFER_LEN ((flags & FLAG_SIZE_T) ? *q2 : *q)
#define RETURN_ERR_OCCURRED return msgbuf
const char* format = *p_format;
char c = *format++;
switch (c) {
case 'b': { // unsigned byte -- very short int
char* p = va_arg(*p_va, char*);
long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
if (ival < 0) {
Thread::current()->raiseWithFmt(
LayoutId::kOverflowError,
"unsigned byte integer is less than minimum");
RETURN_ERR_OCCURRED;
}
if (ival > UCHAR_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kOverflowError,
"unsigned byte integer is greater than maximum");
RETURN_ERR_OCCURRED;
}
*p = static_cast<unsigned char>(ival);
break;
}
case 'B': { // byte sized bitfield - both signed and unsigned
// values allowed
char* p = va_arg(*p_va, char*);
long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsUnsignedLongMask(arg);
if (ival == -1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = static_cast<unsigned char>(ival);
break;
}
case 'h': { // signed short int
short* p = va_arg(*p_va, short*);
long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
if (ival < SHRT_MIN) {
Thread::current()->raiseWithFmt(
LayoutId::kOverflowError,
"signed short integer is less than minimum");
RETURN_ERR_OCCURRED;
}
if (ival > SHRT_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kOverflowError,
"signed short integer is greater than maximum");
RETURN_ERR_OCCURRED;
}
*p = static_cast<short>(ival);
break;
}
case 'H': { // short int sized bitfield, both signed and
// unsigned allowed
unsigned short* p = va_arg(*p_va, unsigned short*);
long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsUnsignedLongMask(arg);
if (ival == -1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = static_cast<unsigned short>(ival);
break;
}
case 'i': { // signed int
int* p = va_arg(*p_va, int*);
long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
if (ival > INT_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kOverflowError, "signed integer is greater than maximum");
RETURN_ERR_OCCURRED;
}
if (ival < INT_MIN) {
Thread::current()->raiseWithFmt(LayoutId::kOverflowError,
"signed integer is less than minimum");
RETURN_ERR_OCCURRED;
}
*p = ival;
break;
}
case 'I': { // int sized bitfield, both signed and
// unsigned allowed
unsigned int* p = va_arg(*p_va, unsigned int*);
unsigned int ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = static_cast<unsigned int>(PyLong_AsUnsignedLongMask(arg));
if (ival == -1U && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = ival;
break;
}
case 'n': // Py_ssize_t
{
PyObject* iobj;
Py_ssize_t* p = va_arg(*p_va, Py_ssize_t*);
Py_ssize_t ival = -1;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
iobj = PyNumber_Index(arg);
if (iobj != nullptr) {
ival = PyLong_AsSsize_t(iobj);
Py_DECREF(iobj);
}
if (ival == -1 && PyErr_Occurred()) RETURN_ERR_OCCURRED;
*p = ival;
break;
}
case 'l': { // long int
long* p = va_arg(*p_va, long*);
long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsLong(arg);
if (ival == -1 && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = ival;
break;
}
case 'k': { // long sized bitfield
unsigned long* p = va_arg(*p_va, unsigned long*);
unsigned long ival;
if (!PyLong_Check(arg)) {
return converterr("int", arg, msgbuf, bufsize);
}
ival = PyLong_AsUnsignedLongMask(arg);
*p = ival;
break;
}
case 'L': { // long long
long long* p = va_arg(*p_va, long long*);
long long ival;
if (float_argument_error(arg)) RETURN_ERR_OCCURRED;
ival = PyLong_AsLongLong(arg);
if (ival == -1LL && PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = ival;
break;
}
case 'K': { // long long sized bitfield
unsigned long long* p = va_arg(*p_va, unsigned long long*);
unsigned long long ival;
if (!PyLong_Check(arg)) {
return converterr("int", arg, msgbuf, bufsize);
}
ival = PyLong_AsUnsignedLongLongMask(arg);
*p = ival;
break;
}
case 'f': { // float
float* p = va_arg(*p_va, float*);
double dval = PyFloat_AsDouble(arg);
if (PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = static_cast<float>(dval);
break;
}
case 'd': { // double
double* p = va_arg(*p_va, double*);
double dval = PyFloat_AsDouble(arg);
if (PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = dval;
break;
}
case 'D': { // complex double
Py_complex* p = va_arg(*p_va, Py_complex*);
Py_complex cval;
cval = PyComplex_AsCComplex(arg);
if (PyErr_Occurred()) {
RETURN_ERR_OCCURRED;
}
*p = cval;
break;
}
case 'c': { // char
char* p = va_arg(*p_va, char*);
if (PyBytes_Check(arg) && PyBytes_Size(arg) == 1) {
*p = PyBytes_AS_STRING(arg)[0];
} else if (PyByteArray_Check(arg) && PyByteArray_Size(arg) == 1) {
*p = PyByteArray_AS_STRING(arg)[0];
} else {
return converterr("a byte string of length 1", arg, msgbuf, bufsize);
}
break;
}
case 'C': { // unicode char
int* p = va_arg(*p_va, int*);
if (!PyUnicode_Check(arg)) {
return converterr("a unicode character", arg, msgbuf, bufsize);
}
if (PyUnicode_READY(arg)) RETURN_ERR_OCCURRED;
if (PyUnicode_GET_LENGTH(arg) != 1) {
return converterr("a unicode character", arg, msgbuf, bufsize);
}
*p = PyUnicode_READ_CHAR(arg, 0);
break;
}
case 'p': { // boolean *p*redicate
int* p = va_arg(*p_va, int*);
int val = PyObject_IsTrue(arg);
if (val > 0) {
*p = 1;
} else if (val == 0) {
*p = 0;
} else {
RETURN_ERR_OCCURRED;
}
break;
}
// XXX WAAAAH! 's', 'y', 'z', 'u', 'Z', 'e', 'w' codes all
// need to be cleaned up!
case 'y': { // any bytes-like object
void const** p =
reinterpret_cast<void const**>(va_arg(*p_va, char const**));
const char* buf;
Py_ssize_t count;
if (*format == '*') {
if (getbuffer(arg, reinterpret_cast<Py_buffer*>(p), &buf) < 0) {
return converterr(buf, arg, msgbuf, bufsize);
}
format++;
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr("(cleanup problem)", arg, msgbuf, bufsize);
}
break;
}
count = convertbuffer(arg, p, &buf);
if (count < 0) {
return converterr(buf, arg, msgbuf, bufsize);
}
if (*format == '#') {
FETCH_SIZE;
STORE_SIZE(count);
format++;
} else {
if (std::strlen(static_cast<const char*>(*p)) !=
static_cast<size_t>(count)) {
Thread::current()->raiseWithFmt(LayoutId::kValueError,
"embedded null byte");
RETURN_ERR_OCCURRED;
}
}
break;
}
case 's': // text string or bytes-like object
case 'z': // text string, bytes-like object or None
{
if (*format == '*') {
// "s*" or "z*"
Py_buffer* p = va_arg(*p_va, Py_buffer*);
if (c == 'z' && arg == Py_None) {
PyBuffer_FillInfo(p, nullptr, nullptr, 0, 1, 0);
} else if (PyUnicode_Check(arg)) {
Py_ssize_t len;
const char* sarg = PyUnicode_AsUTF8AndSize(arg, &len);
if (sarg == nullptr) {
return converterr(CONV_UNICODE, arg, msgbuf, bufsize);
}
// This const_cast is gross, but FillInfo should only ever read from
// this arg.
PyBuffer_FillInfo(p, arg, const_cast<char*>(sarg), len, 1, 0);
} else { // any bytes-like object
const char* buf;
if (getbuffer(arg, p, &buf) < 0) {
return converterr(buf, arg, msgbuf, bufsize);
}
}
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr("(cleanup problem)", arg, msgbuf, bufsize);
}
format++;
} else if (*format == '#') { // a string or read-only bytes-like object
// "s#" or "z#"
void const** p =
reinterpret_cast<void const**>(va_arg(*p_va, char const**));
FETCH_SIZE;
if (c == 'z' && arg == Py_None) {
*p = nullptr;
STORE_SIZE(0);
} else if (PyUnicode_Check(arg)) {
Py_ssize_t len;
const char* sarg = PyUnicode_AsUTF8AndSize(arg, &len);
if (sarg == nullptr) {
return converterr(CONV_UNICODE, arg, msgbuf, bufsize);
}
*p = sarg;
STORE_SIZE(len);
} else { // read-only bytes-like object
// XXX Really?
const char* buf;
Py_ssize_t count = convertbuffer(arg, p, &buf);
if (count < 0) return converterr(buf, arg, msgbuf, bufsize);
STORE_SIZE(count);
}
format++;
} else {
// "s" or "z"
char const** p = va_arg(*p_va, char const**);
Py_ssize_t len;
if (c == 'z' && arg == Py_None) {
*p = nullptr;
} else if (PyUnicode_Check(arg)) {
const char* sarg = PyUnicode_AsUTF8AndSize(arg, &len);
if (sarg == nullptr) {
return converterr(CONV_UNICODE, arg, msgbuf, bufsize);
}
if (std::strlen(sarg) != static_cast<size_t>(len)) {
Thread::current()->raiseWithFmt(LayoutId::kValueError,
"embedded null character");
RETURN_ERR_OCCURRED;
}
*p = sarg;
} else {
return converterr(c == 'z' ? "str or None" : "str", arg, msgbuf,
bufsize);
}
}
break;
}
case 'u': // raw unicode buffer (Py_UNICODE *)
case 'Z': // raw unicode buffer or None
{
Py_UNICODE** p = va_arg(*p_va, Py_UNICODE**);
if (*format == '#') {
// "u#" or "Z#"
FETCH_SIZE;
if (c == 'Z' && arg == Py_None) {
*p = nullptr;
STORE_SIZE(0);
} else if (PyUnicode_Check(arg)) {
Py_ssize_t len;
*p = PyUnicode_AsUnicodeAndSize(arg, &len);
if (*p == nullptr) RETURN_ERR_OCCURRED;
STORE_SIZE(len);
} else {
return converterr(c == 'Z' ? "str or None" : "str", arg, msgbuf,
bufsize);
}
format++;
} else {
// "u" or "Z"
if (c == 'Z' && arg == Py_None) {
*p = nullptr;
} else if (PyUnicode_Check(arg)) {
Py_ssize_t len;
*p = PyUnicode_AsUnicodeAndSize(arg, &len);
if (*p == nullptr) RETURN_ERR_OCCURRED;
if (Py_UNICODE_strlen(*p) != static_cast<size_t>(len)) {
Thread::current()->raiseWithFmt(LayoutId::kValueError,
"embedded null character");
RETURN_ERR_OCCURRED;
}
} else {
return converterr(c == 'Z' ? "str or None" : "str", arg, msgbuf,
bufsize);
}
}
break;
}
case 'e': { // encoded string
char** buffer;
const char* encoding;
PyObject* s;
int recode_strings;
Py_ssize_t size;
const char* ptr;
// Get 'e' parameter: the encoding name
encoding = va_arg(*p_va, const char*);
if (encoding == nullptr) encoding = PyUnicode_GetDefaultEncoding();
// Get output buffer parameter:
// 's' (recode all objects via Unicode) or
// 't' (only recode non-string objects)
if (*format == 's') {
recode_strings = 1;
} else if (*format == 't') {
recode_strings = 0;
} else {
return converterr("(unknown parser marker combination)", arg, msgbuf,
bufsize);
}
buffer = va_arg(*p_va, char**);
format++;
if (buffer == nullptr) {
return converterr("(buffer is nullptr)", arg, msgbuf, bufsize);
}
// Encode object
if (!recode_strings && (PyBytes_Check(arg) || PyByteArray_Check(arg))) {
s = arg;
Py_INCREF(s);
if (PyObject_AsCharBuffer(s, &ptr, &size) < 0) {
return converterr("(AsCharBuffer failed)", arg, msgbuf, bufsize);
}
} else if (PyUnicode_Check(arg)) {
// Encode object; use default error handling
s = PyUnicode_AsEncodedString(arg, encoding, nullptr);
if (s == nullptr) {
return converterr("(encoding failed)", arg, msgbuf, bufsize);
}
assert(PyBytes_Check(s));
size = PyBytes_GET_SIZE(s);
ptr = PyBytes_AS_STRING(s);
if (ptr == nullptr) ptr = "";
} else {
return converterr(recode_strings ? "str" : "str, bytes or bytearray",
arg, msgbuf, bufsize);
}
// Write output; output is guaranteed to be 0-terminated
if (*format == '#') {
// Using buffer length parameter '#':
//
// - if *buffer is nullptr, a new buffer of the
// needed size is allocated and the data
// copied into it; *buffer is updated to point
// to the new buffer; the caller is
// responsible for PyMem_Free()ing it after
// usage
//
// - if *buffer is not nullptr, the data is
// copied to *buffer; *buffer_len has to be
// set to the size of the buffer on input;
// buffer overflow is signalled with an error;
// buffer has to provide enough room for the
// encoded string plus the trailing 0-byte
//
// - in both cases, *buffer_len is updated to
// the size of the buffer /excluding/ the
// trailing 0-byte
FETCH_SIZE;
format++;
if (q == nullptr && q2 == nullptr) {
Py_DECREF(s);
return converterr("(buffer_len is nullptr)", arg, msgbuf, bufsize);
}
if (*buffer == nullptr) {
*buffer = PyMem_NEW(char, size + 1);
if (*buffer == nullptr) {
Py_DECREF(s);
PyErr_NoMemory();
RETURN_ERR_OCCURRED;
}
if (addcleanup(*buffer, freelist, cleanup_ptr)) {
Py_DECREF(s);
return converterr("(cleanup problem)", arg, msgbuf, bufsize);
}
} else {
if (size + 1 > BUFFER_LEN) {
Py_DECREF(s);
PyErr_Format(PyExc_ValueError,
"encoded string too long "
"(%zd, maximum length %zd)",
size, static_cast<Py_ssize_t>(BUFFER_LEN - 1));
RETURN_ERR_OCCURRED;
}
}
memcpy(*buffer, ptr, size + 1);
STORE_SIZE(size);
} else {
// Using a 0-terminated buffer:
//
// - the encoded string has to be 0-terminated
// for this variant to work; if it is not, an
// error raised
//
// - a new buffer of the needed size is
// allocated and the data copied into it;
// *buffer is updated to point to the new
// buffer; the caller is responsible for
// PyMem_Free()ing it after usage
if (static_cast<Py_ssize_t>(std::strlen(ptr)) != size) {
Py_DECREF(s);
return converterr("encoded string without null bytes", arg, msgbuf,
bufsize);
}
*buffer = PyMem_NEW(char, size + 1);
if (*buffer == nullptr) {
Py_DECREF(s);
PyErr_NoMemory();
RETURN_ERR_OCCURRED;
}
if (addcleanup(*buffer, freelist, cleanup_ptr)) {
Py_DECREF(s);
return converterr("(cleanup problem)", arg, msgbuf, bufsize);
}
memcpy(*buffer, ptr, size + 1);
}
Py_DECREF(s);
break;
}
case 'S': { // PyBytes object
PyObject** p = va_arg(*p_va, PyObject**);
if (PyBytes_Check(arg)) {
*p = arg;
} else {
return converterr("bytes", arg, msgbuf, bufsize);
}
break;
}
case 'Y': { // PyByteArray object
PyObject** p = va_arg(*p_va, PyObject**);
if (PyByteArray_Check(arg)) {
*p = arg;
} else {
return converterr("bytearray", arg, msgbuf, bufsize);
}
break;
}
case 'U': { // PyUnicode object
PyObject** p = va_arg(*p_va, PyObject**);
if (PyUnicode_Check(arg)) {
if (PyUnicode_READY(arg) == -1) RETURN_ERR_OCCURRED;
*p = arg;
} else {
return converterr("str", arg, msgbuf, bufsize);
}
break;
}
case 'O': { // object
PyTypeObject* type;
PyObject** p;
if (*format == '!') {
type = va_arg(*p_va, PyTypeObject*);
p = va_arg(*p_va, PyObject**);
format++;
if (PyType_IsSubtype(Py_TYPE(arg), type)) {
*p = arg;
} else {
return converterr(_PyType_Name(type), arg, msgbuf, bufsize);
}
} else if (*format == '&') {
typedef int (*converter)(PyObject*, void*);
converter convert = va_arg(*p_va, converter);
void* addr = va_arg(*p_va, void*);
int res;
format++;
if (!(res = (*convert)(arg, addr))) {
return converterr("(unspecified)", arg, msgbuf, bufsize);
}
if (res == Py_CLEANUP_SUPPORTED &&
addcleanup(addr, freelist, convert) == -1) {
return converterr("(cleanup problem)", arg, msgbuf, bufsize);
}
} else {
p = va_arg(*p_va, PyObject**);
*p = arg;
}
break;
}
case 'w': { // "w*": memory buffer, read-write access
void** p = va_arg(*p_va, void**);
if (*format != '*') {
return converterr("(invalid use of 'w' format character)", arg, msgbuf,
bufsize);
}
format++;
// Caller is interested in Py_buffer, and the object
// supports it directly.
if (PyObject_GetBuffer(arg, reinterpret_cast<Py_buffer*>(p),
PyBUF_WRITABLE) < 0) {
PyErr_Clear();
return converterr("read-write bytes-like object", arg, msgbuf, bufsize);
}
if (!PyBuffer_IsContiguous(reinterpret_cast<Py_buffer*>(p), 'C')) {
PyBuffer_Release(reinterpret_cast<Py_buffer*>(p));
return converterr("contiguous buffer", arg, msgbuf, bufsize);
}
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr("(cleanup problem)", arg, msgbuf, bufsize);
}
break;
}
default:
return converterr("(impossible<bad format char>)", arg, msgbuf, bufsize);
}
*p_format = format;
return nullptr;
#undef FETCH_SIZE
#undef STORE_SIZE
#undef BUFFER_LEN
#undef RETURN_ERR_OCCURRED
}
static Py_ssize_t convertbuffer(PyObject* arg, void const** p,
const char** errmsg) {
Py_ssize_t count;
Py_buffer view;
*errmsg = nullptr;
*p = nullptr;
// TODO(miro): check that the bytes-like object is read-only and fail with not
if (getbuffer(arg, &view, errmsg) < 0) return -1;
count = view.len;
*p = view.buf;
PyBuffer_Release(&view);
return count;
}
static int getbuffer(PyObject* arg, Py_buffer* view, const char** errmsg) {
if (PyObject_GetBuffer(arg, view, PyBUF_SIMPLE) != 0) {
*errmsg = "bytes-like object";
return -1;
}
if (!PyBuffer_IsContiguous(view, 'C')) {
PyBuffer_Release(view);
*errmsg = "contiguous buffer";
return -1;
}
return 0;
}
// Support for keyword arguments donated by
// Geoff Philbrick <philbric@delphi.hks.com>
// Return false (0) for error, else true.
PY_EXPORT int PyArg_ParseTupleAndKeywords(PyObject* args, PyObject* keywords,
const char* format, char** kwlist,
...) {
int retval;
va_list va;
if ((args == nullptr || !PyTuple_Check(args)) ||
(keywords != nullptr && !PyDict_Check(keywords)) || format == nullptr ||
kwlist == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_start(va, kwlist);
retval = vgetargskeywords(args, keywords, format, kwlist, &va, 0);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseTupleAndKeywords_SizeT(PyObject* args,
PyObject* keywords,
const char* format,
char** kwlist, ...) {
int retval;
va_list va;
if ((args == nullptr || !PyTuple_Check(args)) ||
(keywords != nullptr && !PyDict_Check(keywords)) || format == nullptr ||
kwlist == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_start(va, kwlist);
retval = vgetargskeywords(args, keywords, format, kwlist, &va, FLAG_SIZE_T);
va_end(va);
return retval;
}
PY_EXPORT int PyArg_VaParseTupleAndKeywords(PyObject* args, PyObject* keywords,
const char* format, char** kwlist,
va_list va) {
int retval;
va_list lva;
if ((args == nullptr || !PyTuple_Check(args)) ||
(keywords != nullptr && !PyDict_Check(keywords)) || format == nullptr ||
kwlist == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_copy(lva, va);
retval = vgetargskeywords(args, keywords, format, kwlist, &lva, 0);
va_end(lva);
return retval;
}
PY_EXPORT int _PyArg_VaParseTupleAndKeywords_SizeT(PyObject* args,
PyObject* keywords,
const char* format,
char** kwlist, va_list va) {
int retval;
va_list lva;
if ((args == nullptr || !PyTuple_Check(args)) ||
(keywords != nullptr && !PyDict_Check(keywords)) || format == nullptr ||
kwlist == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_copy(lva, va);
retval = vgetargskeywords(args, keywords, format, kwlist, &lva, FLAG_SIZE_T);
va_end(lva);
return retval;
}
PY_EXPORT int _PyArg_ParseTupleAndKeywordsFast(PyObject* args,
PyObject* keywords,
struct _PyArg_Parser* parser,
...) {
if ((args == nullptr || !PyTuple_Check(args)) ||
(keywords != nullptr && !PyDict_Check(keywords)) || parser == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_list va;
va_start(va, parser);
int retval = vGetArgsKeywordsFast(args, keywords, parser, &va, 0);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseTupleAndKeywordsFast_SizeT(
PyObject* args, PyObject* keywords, struct _PyArg_Parser* parser, ...) {
if ((args == nullptr || !PyTuple_Check(args)) ||
(keywords != nullptr && !PyDict_Check(keywords)) || parser == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_list va;
va_start(va, parser);
int retval = vGetArgsKeywordsFast(args, keywords, parser, &va, FLAG_SIZE_T);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseStackAndKeywords(PyObject* const* args,
Py_ssize_t nargs, PyObject* kwnames,
struct _PyArg_Parser* parser, ...) {
if ((kwnames != nullptr && !PyTuple_Check(kwnames)) || parser == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_list va;
va_start(va, parser);
int retval =
vGetArgsKeywordsFastImpl(args, nargs, nullptr, kwnames, parser, &va, 0);
va_end(va);
return retval;
}
PY_EXPORT int _PyArg_ParseStackAndKeywords_SizeT(PyObject* const* args,
Py_ssize_t nargs,
PyObject* kwnames,
struct _PyArg_Parser* parser,
...) {
if ((kwnames != nullptr && !PyTuple_Check(kwnames)) || parser == nullptr) {
PyErr_BadInternalCall();
return 0;
}
va_list va;
va_start(va, parser);
int retval = vGetArgsKeywordsFastImpl(args, nargs, nullptr, kwnames, parser,
&va, FLAG_SIZE_T);
va_end(va);
return retval;
}
#define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':')
static bool isValidKeyword(struct _PyArg_Parser* parser,
Py_ssize_t num_keywords, PyObject* key) {
word start = parser->pos;
for (word i = 0; i < num_keywords; i++) {
if (_PyUnicode_EqualToASCIIString(key, parser->keywords[i + start])) {
return true;
}
}
return false;
}
static int vGetArgsKeywordsFast(PyObject* args, PyObject* keywords,
struct _PyArg_Parser* parser, va_list* p_va,
int flags) {
DCHECK(args != nullptr && PyTuple_Check(args),
"args must be a non-null tuple");
PyObject* small_array[kMaxSmallArraySize];
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
PyObject** stack =
nargs <= kMaxSmallArraySize ? small_array : new PyObject*[nargs];
for (Py_ssize_t i = 0; i < nargs; i++) {
stack[i] = PyTuple_GET_ITEM(args, i);
}
int result = vGetArgsKeywordsFastImpl(stack, nargs, keywords, nullptr, parser,
p_va, flags);
if (stack != small_array) {
delete[] stack;
}
return result;
}
static int vGetArgsKeywordsFastImpl(PyObject* const* args, Py_ssize_t nargs,
PyObject* keywords, PyObject* kwnames,
struct _PyArg_Parser* parser, va_list* p_va,
int flags) {
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
freelist_t freelist;
freelist.entries = static_entries;
freelist.first_available = 0;
freelist.entries_malloced = 0;
DCHECK(keywords == nullptr || PyDict_Check(keywords),
"keywords must be null or a dict");
DCHECK(kwnames == nullptr || PyTuple_Check(kwnames),
"kwnames must be null or a tuple");
DCHECK(keywords == nullptr || kwnames == nullptr,
"both keywords and kwnames cannot be non-null");
DCHECK(parser != nullptr, "parser must not be null");
DCHECK(p_va != nullptr, "p_va must not be null");
int keyword_count = 0;
if (!parserInit(parser, &keyword_count)) {
return false;
}
int pos = parser->pos;
int len = pos + keyword_count;
if (len > STATIC_FREELIST_ENTRIES) {
freelist.entries = PyMem_NEW(freelistentry_t, len);
if (freelist.entries == nullptr) {
PyErr_NoMemory();
return 0;
}
freelist.entries_malloced = 1;
}
Py_ssize_t num_keywords = 0;
PyObject* const* kwstack = nullptr;
if (keywords != nullptr) {
num_keywords = PyDict_Size(keywords);
} else if (kwnames != nullptr) {
num_keywords = PyTuple_GET_SIZE(kwnames);
kwstack = args + nargs;
}
if (nargs + num_keywords > len) {
PyErr_Format(PyExc_TypeError,
"%s%s takes at most %d argument%s (%zd given)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()", len,
(len == 1) ? "" : "s", nargs + num_keywords);
return cleanreturn(0, &freelist);
}
if (parser->max < nargs) {
PyErr_Format(
PyExc_TypeError, "Function takes %s %d positional arguments (%d given)",
(parser->min != INT_MAX) ? "at most" : "exactly", parser->max, nargs);
return cleanreturn(0, &freelist);
}
// convert tuple args and keyword args in same loop, using kwtuple to drive
// process
const char* format = parser->format;
for (int i = 0; i < len; i++) {
const char* keyword = i >= pos ? parser->keywords[i] : nullptr;
if (*format == '|') {
format++;
}
if (*format == '$') {
format++;
}
DCHECK(!IS_END_OF_FORMAT(*format), "end of format was reached");
PyObject* current_arg = nullptr;
if (num_keywords && i >= pos) {
if (keywords != nullptr) {
current_arg = PyDict_GetItemString(keywords, keyword);
if (current_arg == nullptr && PyErr_Occurred()) {
return cleanreturn(0, &freelist);
}
} else {
current_arg = findKeyword(kwnames, kwstack, keyword);
}
}
if (current_arg != nullptr) {
--num_keywords;
if (i < nargs) {
// arg present in tuple and in dict
PyErr_Format(PyExc_TypeError,
"Argument given by name ('%s') "
"and position (%d)",
keyword, i + 1);
return cleanreturn(0, &freelist);
}
} else if (i < nargs) {
current_arg = args[i];
}
if (current_arg != nullptr) {
char msgbuf[512];
int levels[32];
const char* msg = convertitem(current_arg, &format, p_va, flags, levels,
msgbuf, sizeof(msgbuf), &freelist);
if (msg) {
seterror(i + 1, msg, levels, parser->fname, parser->custom_msg);
return cleanreturn(0, &freelist);
}
continue;
}
if (i < parser->min) {
// Less arguments than required
if (i < pos) {
PyErr_Format(
PyExc_TypeError,
"Function takes %s %d positional arguments"
" (%d given)",
(Py_MIN(pos, parser->min) < parser->max) ? "at least" : "exactly",
Py_MIN(pos, parser->min), nargs);
} else {
PyErr_Format(PyExc_TypeError,
"Required argument "
"'%s' (pos %d) not found",
keyword, i + 1);
}
return cleanreturn(0, &freelist);
}
// current code reports success when all required args fulfilled and no
// keyword args left, with no further validation.
if (!num_keywords) {
return cleanreturn(1, &freelist);
}
// We are into optional args, skip through to any remaining keyword args
const char* message = skipitem(&format, p_va, flags);
DCHECK(message == nullptr, "message was not null");
}
DCHECK(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$'),
"expected end of format, '|', '$'");
// make sure there are no extraneous keyword arguments
if (num_keywords > 0) {
if (keywords != nullptr) {
PyObject *key, *value;
Py_ssize_t pos1 = 0;
while (PyDict_Next(keywords, &pos1, &key, &value)) {
if (!PyUnicode_Check(key)) {
Thread::current()->raiseWithFmt(LayoutId::kTypeError,
"keywords must be strings");
return cleanreturn(0, &freelist);
}
if (!isValidKeyword(parser, keyword_count, key)) {
PyErr_Format(PyExc_TypeError,
"'%s' is an invalid keyword "
"argument for this function",
key);
return cleanreturn(0, &freelist);
}
}
} else {
Py_ssize_t num_kwargs = PyTuple_GET_SIZE(kwnames);
for (Py_ssize_t j = 0; j < num_kwargs; j++) {
PyObject* key = PyTuple_GET_ITEM(kwnames, j);
if (!PyUnicode_Check(key)) {
Thread::current()->raiseWithFmt(LayoutId::kTypeError,
"keywords must be strings");
return cleanreturn(0, &freelist);
}
if (!isValidKeyword(parser, keyword_count, key)) {
PyErr_Format(PyExc_TypeError,
"'%U' is an invalid keyword "
"argument for this function",
key);
return cleanreturn(0, &freelist);
}
}
}
}
return cleanreturn(1, &freelist);
}
static int vgetargskeywords(PyObject* args, PyObject* keywords,
const char* format, char** kwlist, va_list* p_va,
int flags) {
char msgbuf[512];
int levels[32];
const char *fname, *msg, *custom_msg, *keyword;
int min = INT_MAX;
int max = INT_MAX;
int i, pos, len;
int skip = 0;
Py_ssize_t nargs, nkeywords;
PyObject* current_arg;
freelistentry_t static_entries[STATIC_FREELIST_ENTRIES];
freelist_t freelist;
freelist.entries = static_entries;
freelist.first_available = 0;
freelist.entries_malloced = 0;
assert(args != nullptr && PyTuple_Check(args));
assert(keywords == nullptr || PyDict_Check(keywords));
assert(format != nullptr);
assert(kwlist != nullptr);
assert(p_va != nullptr);
// grab the function name or custom error msg first (mutually exclusive)
fname = strchr(format, ':');
if (fname) {
fname++;
custom_msg = nullptr;
} else {
custom_msg = strchr(format, ';');
if (custom_msg) custom_msg++;
}
// scan kwlist and count the number of positional-only parameters
for (pos = 0; kwlist[pos] && !*kwlist[pos]; pos++) {
}
// scan kwlist and get greatest possible nbr of args
for (len = pos; kwlist[len]; len++) {
if (!*kwlist[len]) {
Thread::current()->raiseWithFmt(LayoutId::kSystemError,
"Empty keyword parameter name");
return cleanreturn(0, &freelist);
}
}
if (len > STATIC_FREELIST_ENTRIES) {
freelist.entries = PyMem_NEW(freelistentry_t, len);
if (freelist.entries == nullptr) {
PyErr_NoMemory();
return 0;
}
freelist.entries_malloced = 1;
}
nargs = PyTuple_GET_SIZE(args);
nkeywords = (keywords == nullptr) ? 0 : PyDict_Size(keywords);
if (nargs + nkeywords > len) {
PyErr_Format(
PyExc_TypeError, "%s%s takes at most %d argument%s (%zd given)",
(fname == nullptr) ? "function" : fname, (fname == nullptr) ? "" : "()",
len, (len == 1) ? "" : "s", nargs + nkeywords);
return cleanreturn(0, &freelist);
}
// convert tuple args and keyword args in same loop, using kwlist to drive
// process
for (i = 0; i < len; i++) {
keyword = kwlist[i];
if (*format == '|') {
if (min != INT_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kSystemError,
"Invalid format string (| specified twice)");
return cleanreturn(0, &freelist);
}
min = i;
format++;
if (max != INT_MAX) {
Thread::current()->raiseWithFmt(LayoutId::kSystemError,
"Invalid format string ($ before |)");
return cleanreturn(0, &freelist);
}
}
if (*format == '$') {
if (max != INT_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kSystemError,
"Invalid format string ($ specified twice)");
return cleanreturn(0, &freelist);
}
max = i;
format++;
if (max < pos) {
Thread::current()->raiseWithFmt(LayoutId::kSystemError,
"Empty parameter name after $");
return cleanreturn(0, &freelist);
}
if (skip) {
// Now we know the minimal and the maximal numbers of
// positional arguments and can raise an exception with
// informative message (see below).
break;
}
if (max < nargs) {
PyErr_Format(PyExc_TypeError,
"Function takes %s %d positional arguments"
" (%d given)",
(min != INT_MAX) ? "at most" : "exactly", max, nargs);
return cleanreturn(0, &freelist);
}
}
if (IS_END_OF_FORMAT(*format)) {
PyErr_Format(PyExc_SystemError,
"More keyword list entries (%d) than "
"format specifiers (%d)",
len, i);
return cleanreturn(0, &freelist);
}
if (!skip) {
current_arg = nullptr;
if (nkeywords && i >= pos) {
current_arg = PyDict_GetItemString(keywords, keyword);
if (!current_arg && PyErr_Occurred()) {
return cleanreturn(0, &freelist);
}
}
if (current_arg) {
--nkeywords;
if (i < nargs) {
// arg present in tuple and in dict
PyErr_Format(PyExc_TypeError,
"Argument given by name ('%s') "
"and position (%d)",
keyword, i + 1);
return cleanreturn(0, &freelist);
}
} else if (i < nargs) {
// Facebook: Use PyTuple_GetItem instead of &PyTuple_GET_ITEM
// (D12953145)
current_arg = PyTuple_GetItem(args, i);
}
if (current_arg) {
msg = convertitem(current_arg, &format, p_va, flags, levels, msgbuf,
sizeof(msgbuf), &freelist);
if (msg) {
seterror(i + 1, msg, levels, fname, custom_msg);
return cleanreturn(0, &freelist);
}
continue;
}
if (i < min) {
if (i < pos) {
assert(min == INT_MAX);
assert(max == INT_MAX);
skip = 1;
// At that moment we still don't know the minimal and
// the maximal numbers of positional arguments. Raising
// an exception is deferred until we encounter | and $
// or the end of the format.
} else {
PyErr_Format(PyExc_TypeError,
"Required argument "
"'%s' (pos %d) not found",
keyword, i + 1);
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 && !skip) {
return cleanreturn(1, &freelist);
}
}
// We are into optional args, skip thru to any remaining
// keyword args
msg = skipitem(&format, p_va, flags);
if (msg) {
PyErr_Format(PyExc_SystemError, "%s: '%s'", msg, format);
return cleanreturn(0, &freelist);
}
}
if (skip) {
PyErr_Format(PyExc_TypeError,
"Function takes %s %d positional arguments"
" (%d given)",
(Py_MIN(pos, min) < i) ? "at least" : "exactly",
Py_MIN(pos, min), nargs);
return cleanreturn(0, &freelist);
}
if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) {
PyErr_Format(PyExc_SystemError,
"more argument specifiers than keyword list entries "
"(remaining format:'%s')",
format);
return cleanreturn(0, &freelist);
}
// make sure there are no extraneous keyword arguments
if (nkeywords > 0) {
PyObject *key, *value;
Py_ssize_t iter_pos = 0;
while (PyDict_Next(keywords, &iter_pos, &key, &value)) {
int match = 0;
if (!PyUnicode_Check(key)) {
Thread::current()->raiseWithFmt(LayoutId::kTypeError,
"keywords must be strings");
return cleanreturn(0, &freelist);
}
for (i = 0; i < len; i++) {
if (*kwlist[i] && _PyUnicode_EqualToASCIIString(key, kwlist[i])) {
match = 1;
break;
}
}
if (!match) {
PyErr_Format(PyExc_TypeError,
"'%U' is an invalid keyword "
"argument for this function",
key);
return cleanreturn(0, &freelist);
}
}
}
return cleanreturn(1, &freelist);
}
static bool parserInit(struct _PyArg_Parser* parser, int* keyword_count) {
DCHECK(parser->keywords != nullptr, "parser->keywords must not be null");
// grab the function name or custom error msg first (mutually exclusive)
const char* format = parser->format;
if (format != nullptr) {
parser->fname = std::strchr(format, ':');
if (parser->fname != nullptr) {
parser->fname++;
parser->custom_msg = nullptr;
} else {
parser->custom_msg = std::strchr(format, ';');
if (parser->custom_msg) parser->custom_msg++;
}
}
const char* const* keywords = parser->keywords;
// scan keywords and count the number of positional-only parameters
parser->pos = 0;
for (int i = 0; keywords[i] != nullptr && !*keywords[i]; i++) {
parser->pos++;
}
// scan keywords and get greatest possible nbr of args
int len = parser->pos;
for (; keywords[len] != nullptr; len++) {
if (*keywords[len] == '\0') {
Thread::current()->raiseWithFmt(LayoutId::kSystemError,
"Empty keyword parameter name");
return false;
}
}
if (format != nullptr) {
int min, max;
min = max = INT_MAX;
for (int i = 0; i < len; i++) {
if (*format == '|') {
if (min != INT_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kSystemError,
"Invalid format string (| specified twice)");
return false;
}
if (max != INT_MAX) {
Thread::current()->raiseWithFmt(LayoutId::kSystemError,
"Invalid format string ($ before |)");
return false;
}
min = i;
format++;
}
if (*format == '$') {
if (max != INT_MAX) {
Thread::current()->raiseWithFmt(
LayoutId::kSystemError,
"Invalid format string ($ specified twice)");
return false;
}
if (i < parser->pos) {
Thread::current()->raiseWithFmt(LayoutId::kSystemError,
"Empty parameter name after $");
return false;
}
max = i;
format++;
}
if (IS_END_OF_FORMAT(*format)) {
PyErr_Format(PyExc_SystemError,
"More keyword list entries (%d) than "
"format specifiers (%d)",
len, i);
return false;
}
const char* msg;
msg = skipitem(&format, nullptr, 0);
if (msg) {
PyErr_Format(PyExc_SystemError, "%s: '%s'", msg, format);
return false;
}
}
parser->min = Py_MIN(min, len);
parser->max = Py_MIN(max, len);
if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) {
PyErr_Format(PyExc_SystemError,
"more argument specifiers than keyword list entries "
"(remaining format:'%s')",
format);
return false;
}
}
*keyword_count = len - parser->pos;
return true;
}
static PyObject* findKeyword(PyObject* kwnames, PyObject* const* kwstack,
const char* key) {
Py_ssize_t num_kwargs = PyTuple_GET_SIZE(kwnames);
for (Py_ssize_t i = 0; i < num_kwargs; i++) {
PyObject* kwname = PyTuple_GET_ITEM(kwnames, i);
if (!PyUnicode_Check(kwname)) {
// ignore non-string keyword keys: an error will be raised above
continue;
}
if (_PyUnicode_EqualToASCIIString(kwname, key)) {
return kwstack[i];
}
}
return nullptr;
}
static const char* skipitem(const char** p_format, va_list* p_va, int flags) {
const char* format = *p_format;
char c = *format++;
switch (c) {
// codes that take a single data pointer as an argument
// (the type of the pointer is irrelevant)
case 'b': // byte -- very short int
case 'B': // byte as bitfield
case 'h': // short int
case 'H': // short int as bitfield
case 'i': // int
case 'I': // int sized bitfield
case 'l': // long int
case 'k': // long int sized bitfield
case 'L': // long long
case 'K': // long long sized bitfield
case 'n': // Py_ssize_t
case 'f': // float
case 'd': // double
case 'D': // complex double
case 'c': // char
case 'C': // unicode char
case 'p': // boolean predicate
case 'S': // string object
case 'Y': // string object
case 'U': // unicode string object
{
if (p_va != nullptr) {
(void)va_arg(*p_va, void*);
}
break;
}
// string codes
case 'e': // string with encoding
{
if (p_va != nullptr) {
va_arg(*p_va, const char*);
}
if (!(*format == 's' ||
*format == 't')) { // after 'e', only 's' and 't' is allowed
goto err;
}
format++;
}
// fall through
case 's': // string
case 'z': // string or None
case 'y': // bytes
case 'u': // unicode string
case 'Z': // unicode string or None
case 'w': // buffer, read-write
{
if (p_va != nullptr) {
(void)va_arg(*p_va, char**);
}
if (*format == '#') {
if (p_va != nullptr) {
if (flags & FLAG_SIZE_T) {
va_arg(*p_va, Py_ssize_t*);
} else {
va_arg(*p_va, int*);
}
}
format++;
} else if ((c == 's' || c == 'z' || c == 'y') && *format == '*') {
format++;
}
break;
}
case 'O': // object
{
if (*format == '!') {
format++;
if (p_va != nullptr) {
va_arg(*p_va, PyTypeObject*);
va_arg(*p_va, PyObject**);
}
} else if (*format == '&') {
typedef int (*converter)(PyObject*, void*);
if (p_va != nullptr) {
va_arg(*p_va, converter);
va_arg(*p_va, void*);
}
format++;
} else {
if (p_va != nullptr) {
va_arg(*p_va, PyObject**);
}
}
break;
}
case '(': // bypass tuple, not handled at all previously
{
const char* msg;
for (;;) {
if (*format == ')') break;
if (IS_END_OF_FORMAT(*format)) {
return "Unmatched left paren in format "
"string";
}
msg = skipitem(&format, p_va, flags);
if (msg) return msg;
}
format++;
break;
}
case ')':
return "Unmatched right paren in format string";
default:
err:
return "impossible<bad format char>";
}
*p_format = format;
return nullptr;
}
PY_EXPORT int PyArg_UnpackTuple(PyObject* args, const char* name,
Py_ssize_t min, Py_ssize_t max, ...) {
Py_ssize_t i, l;
PyObject** o;
va_list vargs;
assert(min >= 0);
assert(min <= max);
if (!PyTuple_Check(args)) {
Thread::current()->raiseWithFmt(
LayoutId::kSystemError,
"PyArg_UnpackTuple() argument list is not a tuple");
return 0;
}
l = PyTuple_GET_SIZE(args);
if (l < min) {
if (name != nullptr) {
PyErr_Format(PyExc_TypeError, "%s expected %s%zd arguments, got %zd",
name, (min == max ? "" : "at least "), min, l);
} else {
PyErr_Format(PyExc_TypeError,
"unpacked tuple should have %s%zd elements,"
" but has %zd",
(min == max ? "" : "at least "), min, l);
}
return 0;
}
if (l == 0) return 1;
if (l > max) {
if (name != nullptr) {
PyErr_Format(PyExc_TypeError, "%s expected %s%zd arguments, got %zd",
name, (min == max ? "" : "at most "), max, l);
} else {
PyErr_Format(PyExc_TypeError,
"unpacked tuple should have %s%zd elements,"
" but has %zd",
(min == max ? "" : "at most "), max, l);
}
return 0;
}
va_start(vargs, max);
for (i = 0; i < l; i++) {
o = va_arg(vargs, PyObject**);
// Facebook: Use PyTuple_GetItem instead of &PyTuple_GET_ITEM (D12953145)
*o = PyTuple_GetItem(args, i);
}
va_end(vargs);
return 1;
}
PY_EXPORT void _PyArg_BadArgument(const char* fname, const char* displayname,
const char* expected, PyObject* arg) {
PyErr_Format(PyExc_TypeError, "%.200s() %.200s must be %.50s, not %.50s",
fname, displayname, expected,
arg == Py_None ? "None" : _PyType_Name(Py_TYPE(arg)));
}
PY_EXPORT int _PyArg_CheckPositional(const char* name, Py_ssize_t nargs,
Py_ssize_t min, Py_ssize_t max) {
DCHECK_BOUND(min, max);
if (nargs < min) {
if (name != nullptr) {
PyErr_Format(PyExc_TypeError, "%.200s expected %s%zd argument%s, got %zd",
name, (min == max ? "" : "at least "), min,
min == 1 ? "" : "s", nargs);
} else {
PyErr_Format(PyExc_TypeError,
"unpacked tuple should have %s%zd element%s,"
" but has %zd",
(min == max ? "" : "at least "), min, min == 1 ? "" : "s",
nargs);
}
return 0;
}
if (nargs > max) {
if (name != nullptr) {
PyErr_Format(PyExc_TypeError, "%.200s expected %s%zd argument%s, got %zd",
name, (min == max ? "" : "at most "), max,
max == 1 ? "" : "s", nargs);
} else {
PyErr_Format(PyExc_TypeError,
"unpacked tuple should have %s%zd element%s,"
" but has %zd",
(min == max ? "" : "at most "), max, max == 1 ? "" : "s",
nargs);
}
return 0;
}
return 1;
}
static int unpackStack(PyObject* const* args, Py_ssize_t nargs,
const char* name, Py_ssize_t min, Py_ssize_t max,
va_list vargs) {
DCHECK(min >= 0, "min must be positive");
DCHECK(min <= max, "min must be <= max");
if (nargs < min) {
if (name != nullptr) {
PyErr_Format(PyExc_TypeError, "%.200s expected %s%zd arguments, got %zd",
name, (min == max ? "" : "at least "), min, nargs);
} else {
PyErr_Format(PyExc_TypeError,
"unpacked tuple should have %s%zd elements,"
" but has %zd",
(min == max ? "" : "at least "), min, nargs);
}
return 0;
}
if (nargs == 0) {
return 1;
}
if (nargs > max) {
if (name != nullptr) {
PyErr_Format(PyExc_TypeError, "%.200s expected %s%zd arguments, got %zd",
name, (min == max ? "" : "at most "), max, nargs);
} else {
PyErr_Format(PyExc_TypeError,
"unpacked tuple should have %s%zd elements,"
" but has %zd",
(min == max ? "" : "at most "), max, nargs);
}
return 0;
}
for (Py_ssize_t i = 0; i < nargs; i++) {
PyObject** o = va_arg(vargs, PyObject**);
*o = args[i];
}
return 1;
}
PY_EXPORT int _PyArg_UnpackStack(PyObject* const* args, Py_ssize_t nargs,
const char* name, Py_ssize_t min,
Py_ssize_t max, ...) {
va_list vargs;
va_start(vargs, max);
int retval = unpackStack(args, nargs, name, min, max, vargs);
va_end(vargs);
return retval;
}
PY_EXPORT int _PyArg_NoKeywords(const char* funcname, PyObject* kwargs) {
if (kwargs == nullptr) {
return 1;
}
if (!PyDict_CheckExact(kwargs)) {
PyErr_BadInternalCall();
return 0;
}
if (PyDict_Size(kwargs) == 0) {
return 1;
}
PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
funcname);
return 0;
}
PY_EXPORT int _PyArg_NoPositional(const char* funcname, PyObject* args) {
if (args == nullptr) {
return 1;
}
if (!PyTuple_CheckExact(args)) {
PyErr_BadInternalCall();
return 0;
}
if (PyTuple_Size(args) == 0) {
return 1;
}
PyErr_Format(PyExc_TypeError, "%.200s() takes no positional arguments",
funcname);
return 0;
}
PY_EXPORT PyObject* const* _PyArg_UnpackKeywords(
PyObject* const* args, Py_ssize_t nargs, PyObject* kwargs,
PyObject* kwnames, struct _PyArg_Parser* parser, int minpos, int maxpos,
int minkw, PyObject** buf) {
DCHECK(kwargs == nullptr || PyDict_Check(kwargs),
"kwargs must be dict or NULL");
DCHECK(kwargs == nullptr || kwnames == nullptr,
"cannot have both kwargs and kwnames");
if (parser == nullptr) {
PyErr_BadInternalCall();
return nullptr;
}
if (kwnames != nullptr && !PyTuple_Check(kwnames)) {
PyErr_BadInternalCall();
return nullptr;
}
if (args == nullptr && nargs == 0) {
args = buf;
}
int keyword_count = 0;
if (!parserInit(parser, &keyword_count)) {
return nullptr;
}
PyObject* kwtuple = parser->kwtuple;
int posonly = parser->pos;
int minposonly = Py_MIN(posonly, minpos);
int maxargs = posonly + keyword_count;
Py_ssize_t nkwargs;
PyObject* const* kwstack = nullptr;
if (kwargs != nullptr) {
nkwargs = PyDict_GET_SIZE(kwargs);
} else if (kwnames != nullptr) {
nkwargs = PyTuple_GET_SIZE(kwnames);
kwstack = args + nargs;
} else {
nkwargs = 0;
}
if (nkwargs == 0 && minkw == 0 && minpos <= nargs && nargs <= maxpos) {
/* Fast path. */
return args;
}
if (nargs + nkwargs > maxargs) {
/* Adding "keyword" (when nargs == 0) prevents producing wrong error
messages in some special cases (see bpo-31229). */
PyErr_Format(PyExc_TypeError,
"%.200s%s takes at most %d %sargument%s (%zd given)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()", maxargs,
(nargs == 0) ? "keyword " : "", (maxargs == 1) ? "" : "s",
nargs + nkwargs);
return nullptr;
}
if (nargs > maxpos) {
if (maxpos == 0) {
PyErr_Format(PyExc_TypeError, "%.200s%s takes no positional arguments",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()");
} else {
PyErr_Format(PyExc_TypeError,
"%.200s%s takes %s %d positional argument%s (%zd given)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()",
(minpos < maxpos) ? "at most" : "exactly", maxpos,
(maxpos == 1) ? "" : "s", nargs);
}
return nullptr;
}
if (nargs < minposonly) {
PyErr_Format(PyExc_TypeError,
"%.200s%s takes %s %d positional argument%s"
" (%zd given)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()",
minposonly < maxpos ? "at least" : "exactly", minposonly,
minposonly == 1 ? "" : "s", nargs);
return nullptr;
}
/* copy tuple args */
for (Py_ssize_t i = 0; i < nargs; i++) {
buf[i] = args[i];
}
/* copy keyword args using kwtuple to drive process */
int reqlimit = minkw ? maxpos + minkw : minpos;
for (int i = Py_MAX(nargs, posonly); i < maxargs; i++) {
PyObject* current_arg;
if (nkwargs) {
const char* keyword = i >= posonly ? parser->keywords[i] : nullptr;
if (kwargs != nullptr) {
current_arg = PyDict_GetItemString(kwargs, keyword);
if (!current_arg && PyErr_Occurred()) {
return nullptr;
}
} else {
current_arg = findKeyword(kwnames, kwstack, keyword);
}
} else if (i >= reqlimit) {
break;
} else {
current_arg = nullptr;
}
buf[i] = current_arg;
if (current_arg != nullptr) {
--nkwargs;
} else if (i < minpos || (maxpos <= i && i < reqlimit)) {
/* Less arguments than required */
const char* keyword = i >= posonly ? parser->keywords[i] : nullptr;
PyErr_Format(PyExc_TypeError,
"%.200s%s missing required "
"argument '%s' (pos %d)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()", keyword, i + 1);
return nullptr;
}
}
if (nkwargs > 0) {
// make sure there are no arguments given by name and position
for (int i = posonly; i < nargs; i++) {
PyObject* current_arg;
const char* keyword = i >= posonly ? parser->keywords[i] : nullptr;
if (kwargs != nullptr) {
current_arg = PyDict_GetItemString(kwargs, keyword);
if (!current_arg && PyErr_Occurred()) {
return nullptr;
}
} else {
current_arg = findKeyword(kwnames, kwstack, keyword);
}
if (current_arg != nullptr) {
// arg present in tuple and in dict
PyErr_Format(PyExc_TypeError,
"argument for %.200s%s given by name ('%s') "
"and position (%d)",
(parser->fname == nullptr) ? "function" : parser->fname,
(parser->fname == nullptr) ? "" : "()", keyword, i + 1);
return nullptr;
}
}
// make sure there are no extraneous keyword arguments
Py_ssize_t j = 0;
for (;;) {
int match;
PyObject* kw;
if (kwargs != nullptr) {
if (!PyDict_Next(kwargs, &j, &kw, nullptr)) break;
} else {
if (j >= PyTuple_GET_SIZE(kwnames)) break;
kw = PyTuple_GET_ITEM(kwnames, j);
j++;
}
if (!PyUnicode_Check(kw)) {
PyErr_SetString(PyExc_TypeError, "keywords must be strings");
return nullptr;
}
match = PySequence_Contains(kwtuple, kw);
if (match <= 0) {
if (!match) {
PyErr_Format(
PyExc_TypeError,
"'%U' is an invalid keyword "
"argument for %.200s%s",
kw, (parser->fname == nullptr) ? "this function" : parser->fname,
(parser->fname == nullptr) ? "" : "()");
}
return nullptr;
}
}
}
return buf;
}
PY_EXPORT int PyArg_ValidateKeywordArguments(PyObject*) {
UNIMPLEMENTED("PyArg_ValidateKeywordArguments");
}
// Adding empty function to be compatible with cpython
PY_EXPORT void _PyArg_Fini(void) { return; }
} // namespace py