diff options
-rw-r--r-- | Doc/c-api/code.rst | 21 | ||||
-rw-r--r-- | Doc/whatsnew/3.11.rst | 6 | ||||
-rw-r--r-- | Lib/test/test_code.py | 3 | ||||
-rw-r--r-- | Objects/codeobject.c | 16 |
4 files changed, 39 insertions, 7 deletions
diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 840b842..407d8b4 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -33,24 +33,33 @@ bound into a function. Return the number of free variables in *co*. -.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) +.. c:function:: PyCodeObject* PyCode_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable) Return a new code object. If you need a dummy code object to create a frame, use :c:func:`PyCode_NewEmpty` instead. Calling :c:func:`PyCode_New` directly - can bind you to a precise Python version since the definition of the bytecode - changes often. + will bind you to a precise Python version since the definition of the bytecode + changes often. The many arguments of this function are inter-dependent in complex + ways, meaning that subtle changes to values are likely to result in incorrect + execution or VM crashes. Use this function only with extreme care. -.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab) + .. versionchanged:: 3.11 + Added ``exceptiontable`` parameter. + +.. c:function:: PyCodeObject* PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *linetable, PyObject *exceptiontable) Similar to :c:func:`PyCode_New`, but with an extra "posonlyargcount" for positional-only arguments. + The same caveats that apply to ``PyCode_New`` also apply to this function. .. versionadded:: 3.8 + .. versionchanged:: 3.11 + Added ``exceptiontable`` parameter. + .. c:function:: PyCodeObject* PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) Return a new empty code object with the specified filename, - function name, and first line number. It is illegal to - :func:`exec` or :func:`eval` the resulting code object. + function name, and first line number. The resulting code + object will raise an ``Exception`` if executed. .. c:function:: int PyCode_Addr2Line(PyCodeObject *co, int byte_offset) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 8d74c9b..c3a8a7e 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1159,6 +1159,12 @@ C API Changes as its second parameter, instead of ``PyFrameObject*``. See :pep:`523` for more details of how to use this function pointer type. +* :c:func:`PyCode_New` and :c:func:`PyCode_NewWithPosOnlyArgs` now take + an additional ``exception_table`` argument. + Using these functions should be avoided, if at all possible. + To get a custom code object: create a code object using the compiler, + then get a modified version with the ``replace`` method. + New Features ------------ diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py index a37ebd2..1bb138e 100644 --- a/Lib/test/test_code.py +++ b/Lib/test/test_code.py @@ -176,6 +176,9 @@ class CodeTest(unittest.TestCase): self.assertEqual(co.co_filename, "filename") self.assertEqual(co.co_name, "funcname") self.assertEqual(co.co_firstlineno, 15) + #Empty code object should raise, but not crash the VM + with self.assertRaises(Exception): + exec(co) @cpython_only def test_closure_injection(self): diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 9a57815..4fc4b8f 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -626,12 +626,20 @@ PyCode_New(int argcount, int kwonlyargcount, exceptiontable); } +static const char assert0[4] = { + LOAD_ASSERTION_ERROR, + 0, + RAISE_VARARGS, + 1 +}; + PyCodeObject * PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) { PyObject *nulltuple = NULL; PyObject *filename_ob = NULL; PyObject *funcname_ob = NULL; + PyObject *code_ob = NULL; PyCodeObject *result = NULL; nulltuple = PyTuple_New(0); @@ -646,13 +654,17 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) if (filename_ob == NULL) { goto failed; } + code_ob = PyBytes_FromStringAndSize(assert0, 4); + if (code_ob == NULL) { + goto failed; + } #define emptystring (PyObject *)&_Py_SINGLETON(bytes_empty) struct _PyCodeConstructor con = { .filename = filename_ob, .name = funcname_ob, .qualname = funcname_ob, - .code = emptystring, + .code = code_ob, .firstlineno = firstlineno, .linetable = emptystring, .consts = nulltuple, @@ -660,6 +672,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) .localsplusnames = nulltuple, .localspluskinds = emptystring, .exceptiontable = emptystring, + .stacksize = 1, }; result = _PyCode_New(&con); @@ -667,6 +680,7 @@ failed: Py_XDECREF(nulltuple); Py_XDECREF(funcname_ob); Py_XDECREF(filename_ob); + Py_XDECREF(code_ob); return result; } |