From d7ff346b52ea2e238ac373326982fae0ec2b9b21 Mon Sep 17 00:00:00 2001 From: Gautham <41098605+ahgamut@users.noreply.github.com> Date: Sat, 30 Oct 2021 11:24:14 +0530 Subject: [PATCH] Add some Python 3.7 backports (#306) * make.com now uses stack size of 2mb * optimize _PyCFunction_FastCallKeywords * backport python@cpython/7fc252adfbedece75f2330bcfdadbf84dee7836f --- third_party/make/lib/xalloc-die.c | 2 +- third_party/make/src/main.c | 2 + third_party/python/Include/abstract.h | 8 +- third_party/python/Include/dictobject.h | 1 + third_party/python/Include/modsupport.h | 8 + third_party/python/Lib/test/test_print.py | 87 --- third_party/python/Objects/abstract.c | 748 ++++++++++++---------- third_party/python/Objects/methodobject.c | 196 ++++-- third_party/python/Python/modsupport.c | 152 ++++- 9 files changed, 700 insertions(+), 504 deletions(-) diff --git a/third_party/make/lib/xalloc-die.c b/third_party/make/lib/xalloc-die.c index 4e1909797..d6e0fd1ad 100644 --- a/third_party/make/lib/xalloc-die.c +++ b/third_party/make/lib/xalloc-die.c @@ -25,7 +25,7 @@ #include "third_party/make/lib/error.h" #include "third_party/make/lib/exitfail.h" -#include "third_party/make/src/gettext.h" +#include "third_party/make/lib/gettext.h" #define _(msgid) gettext (msgid) void diff --git a/third_party/make/src/main.c b/third_party/make/src/main.c index d7e9bebec..11c47d569 100644 --- a/third_party/make/src/main.c +++ b/third_party/make/src/main.c @@ -26,6 +26,7 @@ this program. If not, see . */ #include "third_party/make/src/getopt.h" #include "libc/sysv/consts/sa.h" +#include "libc/runtime/stack.h" #include "third_party/gdtoa/gdtoa.h" #include @@ -1057,6 +1058,7 @@ int main (int argc, char **argv, char **envp) #endif { + STATIC_STACK_SIZE(0x00200000); // 2mb stack static char *stdin_nm = 0; int makefile_status = MAKE_SUCCESS; struct goaldep *read_files; diff --git a/third_party/python/Include/abstract.h b/third_party/python/Include/abstract.h index bdca099b3..0f69acb95 100644 --- a/third_party/python/Include/abstract.h +++ b/third_party/python/Include/abstract.h @@ -16,8 +16,12 @@ PyObject *PyObject_Call(PyObject *callable_object, PyObject *args, PyObject *_PyStack_AsTuple(PyObject **stack, Py_ssize_t nargs); PyObject *_PyStack_AsDict(PyObject **values, PyObject *kwnames); -int _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, - PyObject ***p_stack, PyObject **p_kwnames); +PyObject ** _PyStack_UnpackDict( + PyObject **args, + Py_ssize_t nargs, + PyObject *kwargs, + PyObject **kwnames, + PyObject *func); PyObject *_PyObject_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, PyObject *kwargs); diff --git a/third_party/python/Include/dictobject.h b/third_party/python/Include/dictobject.h index c649f213a..0a4968d98 100644 --- a/third_party/python/Include/dictobject.h +++ b/third_party/python/Include/dictobject.h @@ -108,6 +108,7 @@ PyObject * PyDict_Copy(PyObject *mp); int PyDict_Contains(PyObject *mp, PyObject *key); #ifndef Py_LIMITED_API int _PyDict_Contains(PyObject *mp, PyObject *key, Py_hash_t hash); +#define PyDict_GET_SIZE(mp) (assert(PyDict_Check(mp)),((PyDictObject *)mp)->ma_used) PyObject * _PyDict_NewPresized(Py_ssize_t minused); void _PyDict_MaybeUntrack(PyObject *mp); int _PyDict_HasOnlyStringKeys(PyObject *mp); diff --git a/third_party/python/Include/modsupport.h b/third_party/python/Include/modsupport.h index cd7242fcf..9d27bc734 100644 --- a/third_party/python/Include/modsupport.h +++ b/third_party/python/Include/modsupport.h @@ -23,6 +23,14 @@ PyObject * _Py_VaBuildValue_SizeT(const char *, va_list); #endif /* !Py_LIMITED_API */ #endif +#define _Py_VaBuildStack _Py_VaBuildStack_SizeT +PyObject ** _Py_VaBuildStack_SizeT( + PyObject **small_stack, + Py_ssize_t small_stack_len, + const char *format, + va_list va, + Py_ssize_t *p_nargs); + /* Due to a glitch in 3.2, the _SizeT versions weren't exported from the DLL. */ #if !defined(PY_SSIZE_T_CLEAN) || !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03030000 int PyArg_Parse(PyObject *, const char *, ...); diff --git a/third_party/python/Lib/test/test_print.py b/third_party/python/Lib/test/test_print.py index e83811221..7eea34911 100644 --- a/third_party/python/Lib/test/test_print.py +++ b/third_party/python/Lib/test/test_print.py @@ -1,5 +1,4 @@ import unittest -import sys from io import StringIO from test import support @@ -129,91 +128,5 @@ class TestPrint(unittest.TestCase): raise RuntimeError self.assertRaises(RuntimeError, print, 1, file=noflush(), flush=True) - -class TestPy2MigrationHint(unittest.TestCase): - """Test that correct hint is produced analogous to Python3 syntax, - if print statement is executed as in Python 2. - """ - - def test_normal_string(self): - python2_print_str = 'print "Hello World"' - with self.assertRaises(SyntaxError) as context: - exec(python2_print_str) - - self.assertIn('print("Hello World")', str(context.exception)) - - def test_string_with_soft_space(self): - python2_print_str = 'print "Hello World",' - with self.assertRaises(SyntaxError) as context: - exec(python2_print_str) - - self.assertIn('print("Hello World", end=" ")', str(context.exception)) - - def test_string_with_excessive_whitespace(self): - python2_print_str = 'print "Hello World", ' - with self.assertRaises(SyntaxError) as context: - exec(python2_print_str) - - self.assertIn('print("Hello World", end=" ")', str(context.exception)) - - def test_string_with_leading_whitespace(self): - python2_print_str = '''if 1: - print "Hello World" - ''' - with self.assertRaises(SyntaxError) as context: - exec(python2_print_str) - - self.assertIn('print("Hello World")', str(context.exception)) - - # bpo-32685: Suggestions for print statement should be proper when - # it is in the same line as the header of a compound statement - # and/or followed by a semicolon - def test_string_with_semicolon(self): - python2_print_str = 'print p;' - with self.assertRaises(SyntaxError) as context: - exec(python2_print_str) - - self.assertIn('print(p)', str(context.exception)) - - def test_string_in_loop_on_same_line(self): - python2_print_str = 'for i in s: print i' - with self.assertRaises(SyntaxError) as context: - exec(python2_print_str) - - self.assertIn('print(i)', str(context.exception)) - - def test_stream_redirection_hint_for_py2_migration(self): - # Test correct hint produced for Py2 redirection syntax - with self.assertRaises(TypeError) as context: - print >> sys.stderr, "message" - self.assertIn('Did you mean "print(, ' - 'file=)"?', str(context.exception)) - - # Test correct hint is produced in the case where RHS implements - # __rrshift__ but returns NotImplemented - with self.assertRaises(TypeError) as context: - print >> 42 - self.assertIn('Did you mean "print(, ' - 'file=)"?', str(context.exception)) - - # Test stream redirection hint is specific to print - with self.assertRaises(TypeError) as context: - max >> sys.stderr - self.assertNotIn('Did you mean ', str(context.exception)) - - # Test stream redirection hint is specific to rshift - with self.assertRaises(TypeError) as context: - print << sys.stderr - self.assertNotIn('Did you mean', str(context.exception)) - - # Ensure right operand implementing rrshift still works - class OverrideRRShift: - def __rrshift__(self, lhs): - return 42 # Force result independent of LHS - - self.assertEqual(print >> OverrideRRShift(), 42) - - - if __name__ == "__main__": unittest.main() diff --git a/third_party/python/Objects/abstract.c b/third_party/python/Objects/abstract.c index 0978bd691..35beea0d5 100644 --- a/third_party/python/Objects/abstract.c +++ b/third_party/python/Objects/abstract.c @@ -41,10 +41,9 @@ type_error(const char *msg, PyObject *obj) static PyObject * null_error(void) { - if (!PyErr_Occurred()) { + if (!PyErr_Occurred()) PyErr_SetString(PyExc_SystemError, "null argument to internal routine"); - } return NULL; } @@ -54,9 +53,11 @@ PyObject * PyObject_Type(PyObject *o) { PyObject *v; + if (o == NULL) { return null_error(); } + v = (PyObject *)o->ob_type; Py_INCREF(v); return v; @@ -66,13 +67,16 @@ Py_ssize_t PyObject_Size(PyObject *o) { PySequenceMethods *m; + if (o == NULL) { null_error(); return -1; } + m = o->ob_type->tp_as_sequence; if (m && m->sq_length) return m->sq_length(o); + return PyMapping_Size(o); } @@ -121,7 +125,7 @@ PyObject_LengthHint(PyObject *o, Py_ssize_t defaultvalue) } return defaultvalue; } - result = PyObject_CallFunctionObjArgs(hint, NULL); + result = _PyObject_CallNoArg(hint); Py_DECREF(hint); if (result == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { @@ -269,14 +273,6 @@ PyObject_DelItemString(PyObject *o, const char *key) /* We release the buffer right after use of this function which could cause issues later on. Don't use these functions in new code. */ -int -PyObject_AsCharBuffer(PyObject *obj, - const char **buffer, - Py_ssize_t *buffer_len) -{ - return PyObject_AsReadBuffer(obj, (const void **)buffer, buffer_len); -} - int PyObject_CheckReadBuffer(PyObject *obj) { @@ -294,9 +290,8 @@ PyObject_CheckReadBuffer(PyObject *obj) return 1; } -int PyObject_AsReadBuffer(PyObject *obj, - const void **buffer, - Py_ssize_t *buffer_len) +static int +as_read_buffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len) { Py_buffer view; @@ -313,6 +308,21 @@ int PyObject_AsReadBuffer(PyObject *obj, return 0; } +int +PyObject_AsCharBuffer(PyObject *obj, + const char **buffer, + Py_ssize_t *buffer_len) +{ + return as_read_buffer(obj, (const void **)buffer, buffer_len); +} + +int PyObject_AsReadBuffer(PyObject *obj, + const void **buffer, + Py_ssize_t *buffer_len) +{ + return as_read_buffer(obj, buffer, buffer_len); +} + int PyObject_AsWriteBuffer(PyObject *obj, void **buffer, Py_ssize_t *buffer_len) @@ -762,38 +772,6 @@ PyNumber_Check(PyObject *o) #define NB_TERNOP(nb_methods, slot) \ (*(ternaryfunc*)(& ((char*)nb_methods)[slot])) -static noinline PyObject * -binop_type_error(PyObject *v, PyObject *w, const char *op_name) -{ - PyErr_Format(PyExc_TypeError, - "unsupported operand type(s) for %.100s: " - "'%.100s' and '%.100s'", - op_name, - v->ob_type->tp_name, - w->ob_type->tp_name); - return NULL; -} - -static noinline PyObject * -binop_not_implemented(PyObject *v, PyObject *w, - const int op_slot, const char *op_name) -{ - if (op_slot == NB_SLOT(nb_rshift) && - PyCFunction_Check(v) && - strcmp(((PyCFunctionObject *)v)->m_ml->ml_name, "print") == 0) - { - PyErr_Format(PyExc_TypeError, - "unsupported operand type(s) for %.100s: " - "'%.100s' and '%.100s'. Did you mean \"print(, " - "file=)\"?", - op_name, - v->ob_type->tp_name, - w->ob_type->tp_name); - return NULL; - } - return binop_type_error(v, w, op_name); -} - /* Calling scheme used for binary operations: @@ -810,6 +788,7 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot) PyObject *x; binaryfunc slotv = NULL; binaryfunc slotw = NULL; + if (v->ob_type->tp_as_number != NULL) slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot); if (w->ob_type != v->ob_type && @@ -840,59 +819,29 @@ binary_op1(PyObject *v, PyObject *w, const int op_slot) Py_RETURN_NOTIMPLEMENTED; } +static PyObject * +binop_type_error(PyObject *v, PyObject *w, const char *op_name) +{ + PyErr_Format(PyExc_TypeError, + "unsupported operand type(s) for %.100s: " + "'%.100s' and '%.100s'", + op_name, + v->ob_type->tp_name, + w->ob_type->tp_name); + return NULL; +} + static PyObject * binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name) { - PyObject *result; - if ((result = binary_op1(v, w, op_slot)) != Py_NotImplemented) { - return result; - } else { + PyObject *result = binary_op1(v, w, op_slot); + if (result == Py_NotImplemented) { Py_DECREF(result); - return binop_not_implemented(v, w, op_slot, op_name); + return binop_type_error(v, w, op_name); } + return result; } -PyObject * -PyNumber_Subtract(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_subtract), "-"); -} - -PyObject * -PyNumber_Or(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_or), "|"); -} - -PyObject * -PyNumber_Xor(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_xor), "^"); -} - -PyObject * -PyNumber_And(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_and), "&"); -} - -PyObject * -PyNumber_Lshift(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_lshift), "<<"); -} - -PyObject * -PyNumber_Rshift(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_rshift), ">>"); -} - -PyObject * -PyNumber_Divmod(PyObject *v, PyObject *w) -{ - return binary_op(v, w, offsetof(PyNumberMethods, nb_divmod), "divmod()"); -} /* Calling scheme used for ternary operations: @@ -913,6 +862,7 @@ ternary_op(PyObject *v, ternaryfunc slotv = NULL; ternaryfunc slotw = NULL; ternaryfunc slotz = NULL; + mv = v->ob_type->tp_as_number; mw = w->ob_type->tp_as_number; if (mv != NULL) @@ -954,6 +904,7 @@ ternary_op(PyObject *v, Py_DECREF(x); /* can't do it */ } } + if (z == Py_None) PyErr_Format( PyExc_TypeError, @@ -972,6 +923,20 @@ ternary_op(PyObject *v, return NULL; } +#define BINARY_FUNC(func, op, op_name) \ + PyObject * \ + func(PyObject *v, PyObject *w) { \ + return binary_op(v, w, NB_SLOT(op), op_name); \ + } + +BINARY_FUNC(PyNumber_Or, nb_or, "|") +BINARY_FUNC(PyNumber_Xor, nb_xor, "^") +BINARY_FUNC(PyNumber_And, nb_and, "&") +BINARY_FUNC(PyNumber_Lshift, nb_lshift, "<<") +BINARY_FUNC(PyNumber_Rshift, nb_rshift, ">>") +BINARY_FUNC(PyNumber_Subtract, nb_subtract, "-") +BINARY_FUNC(PyNumber_Divmod, nb_divmod, "divmod()") + PyObject * PyNumber_Add(PyObject *v, PyObject *w) { @@ -1317,16 +1282,11 @@ PyNumber_Index(PyObject *item) Py_ssize_t PyNumber_AsSsize_t(PyObject *item, PyObject *err) { - PyObject *value; - PyObject *runerr; Py_ssize_t result; - - if (item && PyLong_Check(item)) { - Py_INCREF(item); - value = item; - } else if (!(value = PyNumber_Index(item))) { + PyObject *runerr; + PyObject *value = PyNumber_Index(item); + if (value == NULL) return -1; - } /* We're done if PyLong_AsSsize_t() returns without error. */ result = PyLong_AsSsize_t(value); @@ -1530,7 +1490,7 @@ PySequence_Check(PyObject *s) { if (PyDict_Check(s)) return 0; - return s->ob_type->tp_as_sequence && + return s != NULL && s->ob_type->tp_as_sequence && s->ob_type->tp_as_sequence->sq_item != NULL; } @@ -2119,9 +2079,11 @@ PyObject * PyMapping_GetItemString(PyObject *o, const char *key) { PyObject *okey, *r; + if (key == NULL) { return null_error(); } + okey = PyUnicode_FromString(key); if (okey == NULL) return NULL; @@ -2135,10 +2097,12 @@ PyMapping_SetItemString(PyObject *o, const char *key, PyObject *value) { PyObject *okey; int r; + if (key == NULL) { null_error(); return -1; } + okey = PyUnicode_FromString(key); if (okey == NULL) return -1; @@ -2151,6 +2115,7 @@ int PyMapping_HasKeyString(PyObject *o, const char *key) { PyObject *v; + v = PyMapping_GetItemString(o, key); if (v) { Py_DECREF(v); @@ -2164,6 +2129,7 @@ int PyMapping_HasKey(PyObject *o, PyObject *key) { PyObject *v; + v = PyObject_GetItem(o, key); if (v) { Py_DECREF(v); @@ -2179,6 +2145,7 @@ PyMapping_Keys(PyObject *o) PyObject *keys; PyObject *fast; _Py_IDENTIFIER(keys); + if (PyDict_CheckExact(o)) return PyDict_Keys(o); keys = _PyObject_CallMethodId(o, &PyId_keys, NULL); @@ -2195,6 +2162,7 @@ PyMapping_Items(PyObject *o) PyObject *items; PyObject *fast; _Py_IDENTIFIER(items); + if (PyDict_CheckExact(o)) return PyDict_Items(o); items = _PyObject_CallMethodId(o, &PyId_items, NULL); @@ -2211,6 +2179,7 @@ PyMapping_Values(PyObject *o) PyObject *values; PyObject *fast; _Py_IDENTIFIER(values); + if (PyDict_CheckExact(o)) return PyDict_Values(o); values = _PyObject_CallMethodId(o, &PyId_values, NULL); @@ -2221,23 +2190,29 @@ PyMapping_Values(PyObject *o) return fast; } +/* Operations on callable objects */ + +/* XXX PyCallable_Check() is in object.c */ + PyObject * -PyObject_CallObject(PyObject *o, PyObject *a) +PyObject_CallObject(PyObject *callable, PyObject *args) { - return PyEval_CallObjectWithKeywords(o, a, NULL); + return PyEval_CallObjectWithKeywords(callable, args, NULL); } PyObject* -(_Py_CheckFunctionResult)(PyObject *func, PyObject *result, const char *where) +(_Py_CheckFunctionResult)(PyObject *callable, PyObject *result, const char *where) { int err_occurred = (PyErr_Occurred() != NULL); - assert((func != NULL) ^ (where != NULL)); + + assert((callable != NULL) ^ (where != NULL)); + if (result == NULL) { if (!err_occurred) { - if (func) + if (callable) PyErr_Format(PyExc_SystemError, "%R returned NULL without setting an error", - func); + callable); else PyErr_Format(PyExc_SystemError, "%s returned NULL without setting an error", @@ -2252,10 +2227,11 @@ PyObject* else { if (err_occurred) { Py_DECREF(result); - if (func) { + + if (callable) { _PyErr_FormatFromCause(PyExc_SystemError, "%R returned a result with an error set", - func); + callable); } else { _PyErr_FormatFromCause(PyExc_SystemError, @@ -2273,105 +2249,125 @@ PyObject* } PyObject * -PyObject_Call(PyObject *func, PyObject *args, PyObject *kwargs) +PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs) { ternaryfunc call; PyObject *result; + /* PyObject_Call() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); assert(PyTuple_Check(args)); assert(kwargs == NULL || PyDict_Check(kwargs)); - call = func->ob_type->tp_call; + + call = callable->ob_type->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", - func->ob_type->tp_name); + callable->ob_type->tp_name); return NULL; } + if (Py_EnterRecursiveCall(" while calling a Python object")) return NULL; - result = (*call)(func, args, kwargs); + + result = (*call)(callable, args, kwargs); + Py_LeaveRecursiveCall(); - return _Py_CheckFunctionResult(func, result, NULL); + + return _Py_CheckFunctionResult(callable, result, NULL); } -PyObject * +/* Issue #29234: Inlining _PyStack_AsTuple() into callers increases their + stack consumption, Disable inlining to optimize the stack consumption. */ +PyObject* noinline _PyStack_AsTuple(PyObject **stack, Py_ssize_t nargs) { + PyObject *args; Py_ssize_t i; - PyObject *item, *args; - if ((args = PyTuple_New(nargs))) { - for (i=0; i < nargs; i++) { - item = stack[i]; - Py_INCREF(item); - PyTuple_SET_ITEM(args, i, item); - } - return args; - } else { - return 0; + + args = PyTuple_New(nargs); + if (args == NULL) { + return NULL; } + + for (i=0; i < nargs; i++) { + PyObject *item = stack[i]; + Py_INCREF(item); + PyTuple_SET_ITEM(args, i, item); + } + + return args; } PyObject * -_PyObject_FastCallDict(PyObject *func, PyObject **args, Py_ssize_t nargs, +_PyObject_FastCallDict(PyObject *callable, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { ternaryfunc call; PyObject *result = NULL; + /* _PyObject_FastCallDict() must not be called with an exception set, because it may clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); - assert(func != NULL); + + assert(callable != NULL); assert(nargs >= 0); assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); + if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; } - if (PyFunction_Check(func)) { - result = _PyFunction_FastCallDict(func, args, nargs, kwargs); + + if (PyFunction_Check(callable)) { + result = _PyFunction_FastCallDict(callable, args, nargs, kwargs); } - else if (PyCFunction_Check(func)) { - result = _PyCFunction_FastCallDict(func, args, nargs, kwargs); + else if (PyCFunction_Check(callable)) { + result = _PyCFunction_FastCallDict(callable, args, nargs, kwargs); } else { - Py_ssize_t i; - PyObject *item, *tuple; + PyObject *tuple; + /* Slow-path: build a temporary tuple */ - call = func->ob_type->tp_call; + call = callable->ob_type->tp_call; if (call == NULL) { PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", - func->ob_type->tp_name); + callable->ob_type->tp_name); goto exit; } - /* [jart] inlined _PyStack_AsTuple b/c profiling */ - if (!(tuple = PyTuple_New(nargs))) return 0; - for (i = 0; i < nargs; i++) { - item = args[i]; - Py_INCREF(item); - PyTuple_SET_ITEM(tuple, i, item); + + tuple = _PyStack_AsTuple(args, nargs); + if (tuple == NULL) { + goto exit; } - result = (*call)(func, tuple, kwargs); + + result = (*call)(callable, tuple, kwargs); Py_DECREF(tuple); - result = _Py_CheckFunctionResult(func, result, NULL); + + result = _Py_CheckFunctionResult(callable, result, NULL); } + exit: Py_LeaveRecursiveCall(); + return result; } -/* Positional arguments are obj followed by args. */ +/* Positional arguments are obj followed by args: + call callable(obj, *args, **kwargs) */ PyObject * -_PyObject_Call_Prepend(PyObject *func, +_PyObject_Call_Prepend(PyObject *callable, PyObject *obj, PyObject *args, PyObject *kwargs) { - PyObject *small_stack[8]; + PyObject *small_stack[5]; PyObject **stack; Py_ssize_t argcount; PyObject *result; + assert(PyTuple_Check(args)); + argcount = PyTuple_GET_SIZE(args); if (argcount + 1 <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { stack = small_stack; @@ -2383,12 +2379,14 @@ _PyObject_Call_Prepend(PyObject *func, return NULL; } } + /* use borrowed references */ stack[0] = obj; memcpy(&stack[1], &PyTuple_GET_ITEM(args, 0), argcount * sizeof(PyObject *)); - result = _PyObject_FastCallDict(func, + + result = _PyObject_FastCallDict(callable, stack, argcount + 1, kwargs); if (stack != small_stack) { @@ -2403,10 +2401,12 @@ _PyStack_AsDict(PyObject **values, PyObject *kwnames) Py_ssize_t nkwargs = PyTuple_GET_SIZE(kwnames); PyObject *kwdict; Py_ssize_t i; - kwdict = PyDict_New(); + + kwdict = _PyDict_NewPresized(nkwargs); if (kwdict == NULL) { return NULL; } + for (i = 0; i < nkwargs; i++) { PyObject *key = PyTuple_GET_ITEM(kwnames, i); PyObject *value = *values++; @@ -2420,38 +2420,44 @@ _PyStack_AsDict(PyObject **values, PyObject *kwnames) return kwdict; } -int +PyObject ** _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, - PyObject ***p_stack, PyObject **p_kwnames) + PyObject **p_kwnames, PyObject *func) { PyObject **stack, **kwstack; Py_ssize_t nkwargs; Py_ssize_t pos, i; PyObject *key, *value; PyObject *kwnames; + assert(nargs >= 0); assert(kwargs == NULL || PyDict_CheckExact(kwargs)); - if (kwargs == NULL || (nkwargs = PyDict_Size(kwargs)) == 0) { - *p_stack = args; + + if (kwargs == NULL || (nkwargs = PyDict_GET_SIZE(kwargs)) == 0) { *p_kwnames = NULL; - return 0; + return args; } + if ((size_t)nargs > PY_SSIZE_T_MAX / sizeof(stack[0]) - (size_t)nkwargs) { PyErr_NoMemory(); - return -1; + return NULL; } + stack = PyMem_Malloc((nargs + nkwargs) * sizeof(stack[0])); if (stack == NULL) { PyErr_NoMemory(); - return -1; + return NULL; } + kwnames = PyTuple_New(nkwargs); if (kwnames == NULL) { PyMem_Free(stack); - return -1; + return NULL; } + /* Copy position arguments (borrowed references) */ memcpy(stack, args, nargs * sizeof(stack[0])); + kwstack = stack + nargs; pos = i = 0; /* This loop doesn't support lookup function mutating the dictionary @@ -2464,53 +2470,125 @@ _PyStack_UnpackDict(PyObject **args, Py_ssize_t nargs, PyObject *kwargs, kwstack[i] = value; i++; } - *p_stack = stack; + *p_kwnames = kwnames; - return 0; + return stack; } PyObject * -_PyObject_FastCallKeywords(PyObject *func, PyObject **stack, Py_ssize_t nargs, +_PyObject_FastCallKeywords(PyObject *callable, PyObject **stack, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *kwdict, *result; - Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); assert(nargs >= 0); assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); - assert((nargs == 0 && nkwargs == 0) || stack != NULL); + /* kwnames must only contains str strings, no subclass, and all keys must - be unique: these are implemented in Python/ceval.c and + be unique: these checks are implemented in Python/ceval.c and _PyArg_ParseStack(). */ - if (PyFunction_Check(func)) { - return _PyFunction_FastCallKeywords(func, stack, nargs, kwnames); + + if (PyFunction_Check(callable)) { + return _PyFunction_FastCallKeywords(callable, stack, nargs, kwnames); } - if (PyCFunction_Check(func)) { - return _PyCFunction_FastCallKeywords(func, stack, nargs, kwnames); + if (PyCFunction_Check(callable)) { + return _PyCFunction_FastCallKeywords(callable, stack, nargs, kwnames); } - if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, kwnames); - if (kwdict == NULL) { + else { + /* Slow-path: build a temporary tuple for positional arguments and a + temporary dictionary for keyword arguments (if any) */ + + ternaryfunc call; + PyObject *argtuple; + PyObject *kwdict, *result; + Py_ssize_t nkwargs; + + result = NULL; + nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + assert((nargs == 0 && nkwargs == 0) || stack != NULL); + + if (Py_EnterRecursiveCall(" while calling a Python object")) { return NULL; } + + call = callable->ob_type->tp_call; + if (call == NULL) { + PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable", + callable->ob_type->tp_name); + goto exit; + } + + argtuple = _PyStack_AsTuple(stack, nargs); + if (argtuple == NULL) { + goto exit; + } + + if (nkwargs > 0) { + kwdict = _PyStack_AsDict(stack + nargs, kwnames); + if (kwdict == NULL) { + Py_DECREF(argtuple); + goto exit; + } + } + else { + kwdict = NULL; + } + + result = (*call)(callable, argtuple, kwdict); + Py_DECREF(argtuple); + Py_XDECREF(kwdict); + + exit: + Py_LeaveRecursiveCall(); + return result; } - else { - kwdict = NULL; - } - result = _PyObject_FastCallDict(func, stack, nargs, kwdict); - Py_XDECREF(kwdict); - return result; } -static PyObject* -call_function_tail(PyObject *callable, PyObject *args) +static PyObject * +_PyObject_CallFunctionVa(PyObject *callable, const char *format, + va_list va, int is_size_t) { + PyObject* small_stack[5]; + const Py_ssize_t small_stack_len = Py_ARRAY_LENGTH(small_stack); + PyObject **stack; + Py_ssize_t nargs, i; PyObject *result; - assert(args != NULL); - if (!PyTuple_Check(args)) { - result = _PyObject_CallArg1(callable, args); + + if (callable == NULL) { + return null_error(); + } + + if (!format || !*format) { + return _PyObject_CallNoArg(callable); + } + + if (is_size_t) { + stack = _Py_VaBuildStack(small_stack, small_stack_len, format, va, &nargs); } else { - result = PyObject_Call(callable, args, NULL); + stack = _Py_VaBuildStack_SizeT(small_stack, small_stack_len, format, va, &nargs); + } + if (stack == NULL) { + return NULL; + } + + if (nargs == 1 && PyTuple_Check(stack[0])) { + /* Special cases for backward compatibility: + - PyObject_CallFunction(func, "O", tuple) calls func(*tuple) + - PyObject_CallFunction(func, "(OOO)", arg1, arg2, arg3) calls + func(*(arg1, arg2, arg3)): func(arg1, arg2, arg3) */ + PyObject *args = stack[0]; + result = _PyObject_FastCall(callable, + &PyTuple_GET_ITEM(args, 0), + PyTuple_GET_SIZE(args)); + } + else { + result = _PyObject_FastCall(callable, stack, nargs); + } + + for (i = 0; i < nargs; ++i) { + Py_DECREF(stack[i]); + } + if (stack != small_stack) { + PyMem_Free(stack); } return result; } @@ -2519,21 +2597,12 @@ PyObject * PyObject_CallFunction(PyObject *callable, const char *format, ...) { va_list va; - PyObject *args, *result; - if (callable == NULL) { - return null_error(); - } - if (!format || !*format) { - return _PyObject_CallNoArg(callable); - } + PyObject *result; + va_start(va, format); - args = Py_VaBuildValue(format, va); + result = _PyObject_CallFunctionVa(callable, format, va, 0); va_end(va); - if (args == NULL) { - return NULL; - } - result = call_function_tail(callable, args); - Py_DECREF(args); + return result; } @@ -2541,192 +2610,165 @@ PyObject * _PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...) { va_list va; - PyObject *args, *result; - if (callable == NULL) { - return null_error(); - } - if (!format || !*format) { - return _PyObject_CallNoArg(callable); - } + PyObject *result; + va_start(va, format); - args = _Py_VaBuildValue_SizeT(format, va); + result = _PyObject_CallFunctionVa(callable, format, va, 1); va_end(va); - if (args == NULL) { - return NULL; - } - result = call_function_tail(callable, args); - Py_DECREF(args); + return result; } static PyObject* -callmethod(PyObject* func, const char *format, va_list va, int is_size_t) +callmethod(PyObject* callable, const char *format, va_list va, int is_size_t) { - PyObject *args, *result; - assert(func != NULL); - if (!PyCallable_Check(func)) { - type_error("attribute of type '%.200s' is not callable", func); + assert(callable != NULL); + + if (!PyCallable_Check(callable)) { + type_error("attribute of type '%.200s' is not callable", callable); return NULL; } - if (!format || !*format) { - return _PyObject_CallNoArg(func); - } - if (is_size_t) { - args = _Py_VaBuildValue_SizeT(format, va); - } - else { - args = Py_VaBuildValue(format, va); - } - if (args == NULL) { - return NULL; - } - result = call_function_tail(func, args); - Py_DECREF(args); - return result; + + return _PyObject_CallFunctionVa(callable, format, va, is_size_t); } PyObject * -PyObject_CallMethod(PyObject *o, const char *name, const char *format, ...) +PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...) { va_list va; - PyObject *func = NULL; - PyObject *retval = NULL; - if (o == NULL || name == NULL) { + PyObject *callable, *retval; + + if (obj == NULL || name == NULL) { return null_error(); } - func = PyObject_GetAttrString(o, name); - if (func == NULL) + + callable = PyObject_GetAttrString(obj, name); + if (callable == NULL) return NULL; + va_start(va, format); - retval = callmethod(func, format, va, 0); + retval = callmethod(callable, format, va, 0); va_end(va); - Py_DECREF(func); + + Py_DECREF(callable); return retval; } PyObject * -_PyObject_CallMethodId(PyObject *o, _Py_Identifier *name, +_PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name, const char *format, ...) { va_list va; - PyObject *func = NULL; - PyObject *retval = NULL; - if (o == NULL || name == NULL) { + PyObject *callable, *retval; + + if (obj == NULL || name == NULL) { return null_error(); } - func = _PyObject_GetAttrId(o, name); - if (func == NULL) + + callable = _PyObject_GetAttrId(obj, name); + if (callable == NULL) return NULL; + va_start(va, format); - retval = callmethod(func, format, va, 0); + retval = callmethod(callable, format, va, 0); va_end(va); - Py_DECREF(func); + + Py_DECREF(callable); return retval; } PyObject * -_PyObject_CallMethod_SizeT(PyObject *o, const char *name, +_PyObject_CallMethod_SizeT(PyObject *obj, const char *name, const char *format, ...) { va_list va; - PyObject *func = NULL; - PyObject *retval; - if (o == NULL || name == NULL) { + PyObject *callable, *retval; + + if (obj == NULL || name == NULL) { return null_error(); } - func = PyObject_GetAttrString(o, name); - if (func == NULL) + + callable = PyObject_GetAttrString(obj, name); + if (callable == NULL) return NULL; + va_start(va, format); - retval = callmethod(func, format, va, 1); + retval = callmethod(callable, format, va, 1); va_end(va); - Py_DECREF(func); + + Py_DECREF(callable); return retval; } PyObject * -_PyObject_CallMethodId_SizeT(PyObject *o, _Py_Identifier *name, +_PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name, const char *format, ...) { va_list va; - PyObject *func = NULL; - PyObject *retval; - if (o == NULL || name == NULL) { + PyObject *callable, *retval; + + if (obj == NULL || name == NULL) { return null_error(); } - func = _PyObject_GetAttrId(o, name); - if (func == NULL) { + + callable = _PyObject_GetAttrId(obj, name); + if (callable == NULL) { return NULL; } + va_start(va, format); - retval = callmethod(func, format, va, 1); + retval = callmethod(callable, format, va, 1); va_end(va); - Py_DECREF(func); + + Py_DECREF(callable); return retval; } -static PyObject ** -objargs_mkstack(PyObject **small_stack, Py_ssize_t small_stack_size, - va_list va, Py_ssize_t *p_nargs) +static PyObject * +object_vacall(PyObject *callable, va_list vargs) { - Py_ssize_t i, n; - va_list countva; + PyObject *small_stack[5]; PyObject **stack; + Py_ssize_t nargs; + PyObject *result; + Py_ssize_t i; + va_list countva; + + if (callable == NULL) { + return null_error(); + } + /* Count the number of arguments */ - va_copy(countva, va); - n = 0; + va_copy(countva, vargs); + nargs = 0; while (1) { PyObject *arg = va_arg(countva, PyObject *); if (arg == NULL) { break; } - n++; + nargs++; } - *p_nargs = n; + va_end(countva); + /* Copy arguments */ - if (n <= small_stack_size) { + if (nargs <= (Py_ssize_t)Py_ARRAY_LENGTH(small_stack)) { stack = small_stack; } else { - stack = PyMem_Malloc(n * sizeof(stack[0])); + stack = PyMem_Malloc(nargs * sizeof(stack[0])); if (stack == NULL) { - va_end(countva); PyErr_NoMemory(); return NULL; } } - for (i = 0; i < n; ++i) { - stack[i] = va_arg(va, PyObject *); - } - va_end(countva); - return stack; -} -PyObject * -PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) -{ - PyObject *small_stack[5]; - PyObject **stack; - Py_ssize_t nargs; - PyObject *result; - va_list vargs; - if (callable == NULL || name == NULL) { - return null_error(); - } - callable = PyObject_GetAttr(callable, name); - if (callable == NULL) - return NULL; - /* count the args */ - va_start(vargs, name); - stack = objargs_mkstack(small_stack, Py_ARRAY_LENGTH(small_stack), - vargs, &nargs); - va_end(vargs); - if (stack == NULL) { - Py_DECREF(callable); - return NULL; + for (i = 0; i < nargs; ++i) { + stack[i] = va_arg(vargs, PyObject *); } + + /* Call the function */ result = _PyObject_FastCall(callable, stack, nargs); - Py_DECREF(callable); + if (stack != small_stack) { PyMem_Free(stack); } @@ -2734,63 +2776,68 @@ PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) } PyObject * -_PyObject_CallMethodIdObjArgs(PyObject *callable, - struct _Py_Identifier *name, ...) +PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) { - PyObject *small_stack[5]; - PyObject **stack; - Py_ssize_t nargs; - PyObject *result; va_list vargs; + PyObject *result; + if (callable == NULL || name == NULL) { return null_error(); } - callable = _PyObject_GetAttrId(callable, name); - if (callable == NULL) + + callable = PyObject_GetAttr(callable, name); + if (callable == NULL) { return NULL; - /* count the args */ + } + va_start(vargs, name); - stack = objargs_mkstack(small_stack, Py_ARRAY_LENGTH(small_stack), - vargs, &nargs); + result = object_vacall(callable, vargs); va_end(vargs); - if (stack == NULL) { - Py_DECREF(callable); + + Py_DECREF(callable); + return result; +} + +PyObject * +_PyObject_CallMethodIdObjArgs(PyObject *obj, + struct _Py_Identifier *name, ...) +{ + va_list vargs; + PyObject *callable, *result; + + if (obj == NULL || name == NULL) { + return null_error(); + } + + callable = _PyObject_GetAttrId(obj, name); + if (callable == NULL) { return NULL; } - result = _PyObject_FastCall(callable, stack, nargs); + + va_start(vargs, name); + result = object_vacall(callable, vargs); + va_end(vargs); + Py_DECREF(callable); - if (stack != small_stack) { - PyMem_Free(stack); - } return result; } PyObject * PyObject_CallFunctionObjArgs(PyObject *callable, ...) { - PyObject *small_stack[5]; - PyObject **stack; - Py_ssize_t nargs; - PyObject *result; va_list vargs; - if (callable == NULL) { - return null_error(); - } - /* count the args */ + PyObject *result; + va_start(vargs, callable); - stack = objargs_mkstack(small_stack, Py_ARRAY_LENGTH(small_stack), - vargs, &nargs); + result = object_vacall(callable, vargs); va_end(vargs); - if (stack == NULL) { - return NULL; - } - result = _PyObject_FastCall(callable, stack, nargs); - if (stack != small_stack) { - PyMem_Free(stack); - } + return result; } + +/* isinstance(), issubclass() */ + /* abstract_get_bases() has logically 4 return states: * * 1. getattr(cls, '__bases__') could raise an AttributeError @@ -2820,6 +2867,7 @@ abstract_get_bases(PyObject *cls) { _Py_IDENTIFIER(__bases__); PyObject *bases; + Py_ALLOW_RECURSION bases = _PyObject_GetAttrId(cls, &PyId___bases__); Py_END_ALLOW_RECURSION @@ -2835,12 +2883,14 @@ abstract_get_bases(PyObject *cls) return bases; } + static int abstract_issubclass(PyObject *derived, PyObject *cls) { PyObject *bases = NULL; Py_ssize_t i, n; int r = 0; + while (1) { if (derived == cls) return 1; @@ -2891,6 +2941,7 @@ recursive_isinstance(PyObject *inst, PyObject *cls) PyObject *icls; int retval = 0; _Py_IDENTIFIER(__class__); + if (PyType_Check(cls)) { retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls); if (retval == 0) { @@ -2927,6 +2978,7 @@ recursive_isinstance(PyObject *inst, PyObject *cls) Py_DECREF(icls); } } + return retval; } @@ -2935,17 +2987,21 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls) { _Py_IDENTIFIER(__instancecheck__); PyObject *checker; + /* Quick test for an exact match */ if (Py_TYPE(inst) == (PyTypeObject *)cls) return 1; + /* We know what type's __instancecheck__ does. */ if (PyType_CheckExact(cls)) { return recursive_isinstance(inst, cls); } + if (PyTuple_Check(cls)) { Py_ssize_t i; Py_ssize_t n; int r = 0; + if (Py_EnterRecursiveCall(" in __instancecheck__")) return -1; n = PyTuple_GET_SIZE(cls); @@ -2959,6 +3015,7 @@ PyObject_IsInstance(PyObject *inst, PyObject *cls) Py_LeaveRecursiveCall(); return r; } + checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__); if (checker != NULL) { PyObject *res; @@ -2996,6 +3053,7 @@ recursive_issubclass(PyObject *derived, PyObject *cls) "issubclass() arg 2 must be a class" " or tuple of classes")) return -1; + return abstract_issubclass(derived, cls); } @@ -3004,6 +3062,7 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls) { _Py_IDENTIFIER(__subclasscheck__); PyObject *checker; + /* We know what type's __subclasscheck__ does. */ if (PyType_CheckExact(cls)) { /* Quick test for an exact match */ @@ -3011,10 +3070,12 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls) return 1; return recursive_issubclass(derived, cls); } + if (PyTuple_Check(cls)) { Py_ssize_t i; Py_ssize_t n; int r = 0; + if (Py_EnterRecursiveCall(" in __subclasscheck__")) return -1; n = PyTuple_GET_SIZE(cls); @@ -3028,6 +3089,7 @@ PyObject_IsSubclass(PyObject *derived, PyObject *cls) Py_LeaveRecursiveCall(); return r; } + checker = _PyObject_LookupSpecial(cls, &PyId___subclasscheck__); if (checker != NULL) { PyObject *res; @@ -3063,11 +3125,13 @@ _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls) return recursive_issubclass(derived, cls); } + PyObject * PyObject_GetIter(PyObject *o) { PyTypeObject *t = o->ob_type; - getiterfunc f = NULL; + getiterfunc f; + f = t->tp_iter; if (f == NULL) { if (PySequence_Check(o)) @@ -3107,6 +3171,7 @@ PyIter_Next(PyObject *iter) return result; } + /* * Flatten a sequence of bytes() objects into a C array of * NULL terminated string pointers with a NULL char* terminating the array. @@ -3122,14 +3187,18 @@ _PySequence_BytesToCharpArray(PyObject* self) Py_ssize_t i, argc; PyObject *item = NULL; Py_ssize_t size; + argc = PySequence_Size(self); if (argc == -1) return NULL; + assert(argc >= 0); + if ((size_t)argc > (PY_SSIZE_T_MAX-sizeof(char *)) / sizeof(char *)) { PyErr_NoMemory(); return NULL; } + array = PyMem_Malloc((argc + 1) * sizeof(char *)); if (array == NULL) { PyErr_NoMemory(); @@ -3143,8 +3212,8 @@ _PySequence_BytesToCharpArray(PyObject* self) array[i] = NULL; goto fail; } - /* check for embedded null bytes */ - if (PyBytes_AsStringAndSize(item, &data, NULL) < 0) { + data = PyBytes_AsString(item); + if (data == NULL) { /* NULL terminate before freeing. */ array[i] = NULL; goto fail; @@ -3159,13 +3228,16 @@ _PySequence_BytesToCharpArray(PyObject* self) Py_DECREF(item); } array[argc] = NULL; + return array; + fail: Py_XDECREF(item); _Py_FreeCharPArray(array); return NULL; } + /* Free's a NULL terminated char** array of C strings. */ void _Py_FreeCharPArray(char *const array[]) diff --git a/third_party/python/Objects/methodobject.c b/third_party/python/Objects/methodobject.c index b8ef1aa5b..ca48ebd73 100644 --- a/third_party/python/Objects/methodobject.c +++ b/third_party/python/Objects/methodobject.c @@ -100,8 +100,9 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) Py_ssize_t size; int flags; + assert(kwds == NULL || PyDict_Check(kwds)); /* PyCFunction_Call() must not be called with an exception set, - because it may clear it (directly or indirectly) and so the + because it can clear it (directly or indirectly) and so the caller loses its exception */ assert(!PyErr_Occurred()); @@ -116,7 +117,7 @@ PyCFunction_Call(PyObject *func, PyObject *args, PyObject *kwds) res = _PyCFunction_FastCallDict(func, stack, nargs, kwds); } else { - if (kwds != NULL && PyDict_Size(kwds) != 0) { + if (kwds != NULL && PyDict_GET_SIZE(kwds) != 0) { PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", f->m_ml->ml_name); return NULL; @@ -167,14 +168,14 @@ PyObject * _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, PyObject *kwargs) { - PyCFunctionObject *func = (PyCFunctionObject*)func_obj; - PyCFunction meth = PyCFunction_GET_FUNCTION(func); - PyObject *self = PyCFunction_GET_SELF(func); + PyCFunctionObject *func; + PyCFunction meth; + PyObject *self; PyObject *result; int flags; - assert(PyCFunction_Check(func)); - assert(func != NULL); + assert(func_obj != NULL); + assert(PyCFunction_Check(func_obj)); assert(nargs >= 0); assert(nargs == 0 || args != NULL); assert(kwargs == NULL || PyDict_Check(kwargs)); @@ -184,21 +185,21 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, caller loses its exception */ assert(!PyErr_Occurred()); + func = (PyCFunctionObject*)func_obj; + meth = PyCFunction_GET_FUNCTION(func); + self = PyCFunction_GET_SELF(func); flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); switch (flags) { case METH_NOARGS: - if (kwargs != NULL && PyDict_Size(kwargs) != 0) { - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - func->m_ml->ml_name); - return NULL; + if (nargs != 0) { + goto no_keyword_error; } - if (nargs != 0) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%zd given)", - func->m_ml->ml_name, nargs); + if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + func->m_ml->ml_name); return NULL; } @@ -206,12 +207,6 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, break; case METH_O: - if (kwargs != NULL && PyDict_Size(kwargs) != 0) { - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - func->m_ml->ml_name); - return NULL; - } - if (nargs != 1) { PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%zd given)", @@ -219,29 +214,27 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, return NULL; } + if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + goto no_keyword_error; + } + result = (*meth) (self, args[0]); break; case METH_VARARGS: case METH_VARARGS | METH_KEYWORDS: { - /* Slow-path: create a temporary tuple */ - Py_ssize_t i; - PyObject *item, *tuple; + /* Slow-path: create a temporary tuple for positional arguments */ + PyObject *tuple; - if (!(flags & METH_KEYWORDS) && kwargs != NULL && PyDict_Size(kwargs) != 0) { - PyErr_Format(PyExc_TypeError, - "%.200s() takes no keyword arguments", - func->m_ml->ml_name); - return NULL; + if (!(flags & METH_KEYWORDS) + && kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + goto no_keyword_error; } - /* [jart] inlined _PyStack_AsTuple b/c profiling */ - if (!(tuple = PyTuple_New(nargs))) return 0; - for (i = 0; i < nargs; i++) { - item = args[i]; - Py_INCREF(item); - PyTuple_SET_ITEM(tuple, i, item); + tuple = _PyStack_AsTuple(args, nargs); + if (tuple == NULL) { + return NULL; } if (flags & METH_KEYWORDS) { @@ -260,7 +253,8 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, PyObject *kwnames; _PyCFunctionFast fastmeth = (_PyCFunctionFast)meth; - if (_PyStack_UnpackDict(args, nargs, kwargs, &stack, &kwnames) < 0) { + stack = _PyStack_UnpackDict(args, nargs, kwargs, &kwnames, func_obj); + if (stack == NULL) { return NULL; } @@ -282,35 +276,134 @@ _PyCFunction_FastCallDict(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, result = _Py_CheckFunctionResult(func_obj, result, NULL); return result; + +no_keyword_error: + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", + func->m_ml->ml_name, nargs); + return NULL; } PyObject * -_PyCFunction_FastCallKeywords(PyObject *func, PyObject **stack, +_PyCFunction_FastCallKeywords(PyObject *func_obj, PyObject **args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *kwdict, *result; + PyCFunctionObject *func; + PyCFunction meth; + PyObject *self, *result; Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames); + int flags; - assert(PyCFunction_Check(func)); + assert(func_obj != NULL); + assert(PyCFunction_Check(func_obj)); assert(nargs >= 0); assert(kwnames == NULL || PyTuple_CheckExact(kwnames)); - assert((nargs == 0 && nkwargs == 0) || stack != NULL); + assert((nargs == 0 && nkwargs == 0) || args != NULL); /* kwnames must only contains str strings, no subclass, and all keys must be unique */ - if (nkwargs > 0) { - kwdict = _PyStack_AsDict(stack + nargs, kwnames); - if (kwdict == NULL) { + /* _PyCFunction_FastCallKeywords() must not be called with an exception + set, because it can clear it (directly or indirectly) and so the caller + loses its exception */ + assert(!PyErr_Occurred()); + + func = (PyCFunctionObject*)func_obj; + meth = PyCFunction_GET_FUNCTION(func); + self = PyCFunction_GET_SELF(func); + flags = PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST); + + switch (flags) + { + case METH_NOARGS: + if (nargs != 0) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%zd given)", + func->m_ml->ml_name, nargs); return NULL; } - } - else { - kwdict = NULL; + + if (nkwargs) { + goto no_keyword_error; + } + + result = (*meth) (self, NULL); + break; + + case METH_O: + if (nargs != 1) { + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%zd given)", + func->m_ml->ml_name, nargs); + return NULL; + } + + if (nkwargs) { + goto no_keyword_error; + } + + result = (*meth) (self, args[0]); + break; + + case METH_FASTCALL: + /* Fast-path: avoid temporary dict to pass keyword arguments */ + result = ((_PyCFunctionFast)meth) (self, args, nargs, kwnames); + break; + + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + { + /* Slow-path: create a temporary tuple for positional arguments + and a temporary dict for keyword arguments */ + PyObject *argtuple; + + if (!(flags & METH_KEYWORDS) && nkwargs) { + goto no_keyword_error; + } + + argtuple = _PyStack_AsTuple(args, nargs); + if (argtuple == NULL) { + return NULL; + } + + if (flags & METH_KEYWORDS) { + PyObject *kwdict; + + if (nkwargs > 0) { + kwdict = _PyStack_AsDict(args + nargs, kwnames); + if (kwdict == NULL) { + Py_DECREF(argtuple); + return NULL; + } + } + else { + kwdict = NULL; + } + + result = (*(PyCFunctionWithKeywords)meth) (self, argtuple, kwdict); + Py_XDECREF(kwdict); + } + else { + result = (*meth) (self, argtuple); + } + Py_DECREF(argtuple); + break; } - result = _PyCFunction_FastCallDict(func, stack, nargs, kwdict); - Py_XDECREF(kwdict); + default: + PyErr_SetString(PyExc_SystemError, + "Bad call flags in _PyCFunction_FastCallKeywords. " + "METH_OLDARGS is no longer supported!"); + return NULL; + } + + result = _Py_CheckFunctionResult(func_obj, result, NULL); return result; + +no_keyword_error: + PyErr_Format(PyExc_TypeError, + "%.200s() takes no keyword arguments", + func->m_ml->ml_name); + return NULL; } /* Methods (the standard built-in methods, that is) */ @@ -337,13 +430,16 @@ meth_dealloc(PyCFunctionObject *m) static PyObject * meth_reduce(PyCFunctionObject *m) { + PyObject *builtins; + PyObject *getattr; _Py_IDENTIFIER(getattr); if (m->m_self == NULL || PyModule_Check(m->m_self)) return PyUnicode_FromString(m->m_ml->ml_name); - return Py_BuildValue("N(Os)", _PyEval_GetBuiltinId(&PyId_getattr), - m->m_self, m->m_ml->ml_name); + builtins = PyEval_GetBuiltins(); + getattr = _PyDict_GetItemId(builtins, &PyId_getattr); + return Py_BuildValue("O(Os)", getattr, m->m_self, m->m_ml->ml_name); } static PyMethodDef meth_methods[] = { diff --git a/third_party/python/Python/modsupport.c b/third_party/python/Python/modsupport.c index a5fe849f4..ddef92303 100644 --- a/third_party/python/Python/modsupport.c +++ b/third_party/python/Python/modsupport.c @@ -13,25 +13,25 @@ #include "third_party/python/Include/longobject.h" #include "third_party/python/Include/object.h" #include "third_party/python/Include/pyerrors.h" +#include "third_party/python/Include/pymem.h" #include "third_party/python/Include/tupleobject.h" /* clang-format off */ -/* Module support implementation */ - #define FLAG_SIZE_T 1 typedef double va_double; static PyObject *va_build_value(const char *, va_list, int); +static PyObject **va_build_stack(PyObject **small_stack, Py_ssize_t small_stack_len, const char *, va_list, int, Py_ssize_t*); /* Package context -- the full module name for package imports */ -char *_Py_PackageContext = NULL; +const char *_Py_PackageContext = NULL; /* Helper for mkvalue() to scan the length of a format */ -static int -countformat(const char *format, int endchar) +static Py_ssize_t +countformat(const char *format, char endchar) { - int count = 0; + Py_ssize_t count = 0; int level = 0; while (level > 0 || *format != endchar) { switch (*format) { @@ -43,8 +43,9 @@ countformat(const char *format, int endchar) case '(': case '[': case '{': - if (level == 0) + if (level == 0) { count++; + } level++; break; case ')': @@ -60,8 +61,9 @@ countformat(const char *format, int endchar) case '\t': break; default: - if (level == 0) + if (level == 0) { count++; + } } format++; } @@ -72,17 +74,18 @@ countformat(const char *format, int endchar) /* Generic function to create a value -- the inverse of getargs() */ /* After an original idea and first implementation by Steven Miale */ -static PyObject *do_mktuple(const char**, va_list *, int, int, int); -static PyObject *do_mklist(const char**, va_list *, int, int, int); -static PyObject *do_mkdict(const char**, va_list *, int, int, int); +static PyObject *do_mktuple(const char**, va_list *, char, Py_ssize_t, int); +static int do_mkstack(PyObject **, const char**, va_list *, char, Py_ssize_t, int); +static PyObject *do_mklist(const char**, va_list *, char, Py_ssize_t, int); +static PyObject *do_mkdict(const char**, va_list *, char, Py_ssize_t, int); static PyObject *do_mkvalue(const char**, va_list *, int); static void -do_ignore(const char **p_format, va_list *p_va, int endchar, int n, int flags) +do_ignore(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n, int flags) { PyObject *v; - int i; + Py_ssize_t i; assert(PyErr_Occurred()); v = PyTuple_New(n); for (i = 0; i < n; i++) { @@ -106,15 +109,16 @@ do_ignore(const char **p_format, va_list *p_va, int endchar, int n, int flags) "Unmatched paren in format"); return; } - if (endchar) + if (endchar) { ++*p_format; + } } static PyObject * -do_mkdict(const char **p_format, va_list *p_va, int endchar, int n, int flags) +do_mkdict(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n, int flags) { PyObject *d; - int i; + Py_ssize_t i; if (n < 0) return NULL; if (n % 2) { @@ -161,10 +165,10 @@ do_mkdict(const char **p_format, va_list *p_va, int endchar, int n, int flags) } static PyObject * -do_mklist(const char **p_format, va_list *p_va, int endchar, int n, int flags) +do_mklist(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n, int flags) { PyObject *v; - int i; + Py_ssize_t i; if (n < 0) return NULL; /* Note that we can't bail immediately on error as this will leak @@ -194,11 +198,48 @@ do_mklist(const char **p_format, va_list *p_va, int endchar, int n, int flags) return v; } +static int +do_mkstack(PyObject **stack, const char **p_format, va_list *p_va, + char endchar, Py_ssize_t n, int flags) +{ + Py_ssize_t i; + + if (n < 0) { + return -1; + } + /* Note that we can't bail immediately on error as this will leak + refcounts on any 'N' arguments. */ + for (i = 0; i < n; i++) { + PyObject *w = do_mkvalue(p_format, p_va, flags); + if (w == NULL) { + do_ignore(p_format, p_va, endchar, n - i - 1, flags); + goto error; + } + stack[i] = w; + } + if (**p_format != endchar) { + PyErr_SetString(PyExc_SystemError, + "Unmatched paren in format"); + goto error; + } + if (endchar) { + ++*p_format; + } + return 0; + +error: + n = i; + for (i=0; i < n; i++) { + Py_DECREF(stack[i]); + } + return -1; +} + static PyObject * -do_mktuple(const char **p_format, va_list *p_va, int endchar, int n, int flags) +do_mktuple(const char **p_format, va_list *p_va, char endchar, Py_ssize_t n, int flags) { PyObject *v; - int i; + Py_ssize_t i; if (n < 0) return NULL; /* Note that we can't bail immediately on error as this will leak @@ -302,7 +343,7 @@ do_mkvalue(const char **p_format, va_list *p_va, int flags) else { if (n < 0) n = wcslen(u); - v = PyUnicode_FromUnicode(u, n); + v = PyUnicode_FromWideChar(u, n); } return v; } @@ -480,7 +521,7 @@ static PyObject * va_build_value(const char *format, va_list va, int flags) { const char *f = format; - int n = countformat(f, '\0'); + Py_ssize_t n = countformat(f, '\0'); va_list lva; PyObject *retval; @@ -500,9 +541,68 @@ va_build_value(const char *format, va_list va, int flags) return retval; } +PyObject ** +_Py_VaBuildStack(PyObject **small_stack, Py_ssize_t small_stack_len, + const char *format, va_list va, Py_ssize_t *p_nargs) +{ + return va_build_stack(small_stack, small_stack_len, format, va, 0, p_nargs); +} + +PyObject ** +_Py_VaBuildStack_SizeT(PyObject **small_stack, Py_ssize_t small_stack_len, + const char *format, va_list va, Py_ssize_t *p_nargs) +{ + return va_build_stack(small_stack, small_stack_len, format, va, FLAG_SIZE_T, p_nargs); +} + +static PyObject ** +va_build_stack(PyObject **small_stack, Py_ssize_t small_stack_len, + const char *format, va_list va, int flags, Py_ssize_t *p_nargs) +{ + const char *f; + Py_ssize_t n; + va_list lva; + PyObject **stack; + int res; + + n = countformat(format, '\0'); + if (n < 0) { + *p_nargs = 0; + return NULL; + } + + if (n == 0) { + *p_nargs = 0; + return small_stack; + } + + if (n <= small_stack_len) { + stack = small_stack; + } + else { + stack = PyMem_Malloc(n * sizeof(stack[0])); + if (stack == NULL) { + PyErr_NoMemory(); + return NULL; + } + } + + va_copy(lva, va); + f = format; + res = do_mkstack(stack, &f, &lva, '\0', n, flags); + va_end(lva); + + if (res < 0) { + return NULL; + } + + *p_nargs = n; + return stack; +} + PyObject * -PyEval_CallFunction(PyObject *obj, const char *format, ...) +PyEval_CallFunction(PyObject *callable, const char *format, ...) { va_list vargs; PyObject *args; @@ -516,7 +616,7 @@ PyEval_CallFunction(PyObject *obj, const char *format, ...) if (args == NULL) return NULL; - res = PyEval_CallObject(obj, args); + res = PyEval_CallObject(callable, args); Py_DECREF(args); return res; @@ -524,14 +624,14 @@ PyEval_CallFunction(PyObject *obj, const char *format, ...) PyObject * -PyEval_CallMethod(PyObject *obj, const char *methodname, const char *format, ...) +PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...) { va_list vargs; PyObject *meth; PyObject *args; PyObject *res; - meth = PyObject_GetAttrString(obj, methodname); + meth = PyObject_GetAttrString(obj, name); if (meth == NULL) return NULL;