From 68307483e5cf91046d66aa8061a5887e06a02c37 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 19 May 2021 16:44:56 -0600 Subject: [PATCH] bpo-43693: Group the code in codeobject.c logically. (gh-26216) --- Objects/codeobject.c | 1202 ++++++++++++++++++++++-------------------- 1 file changed, 626 insertions(+), 576 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e981e39aaf1..6ac11562254 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -10,16 +10,10 @@ #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "clinic/codeobject.c.h" -/* Holder for co_extra information */ -typedef struct { - Py_ssize_t ce_size; - void *ce_extras[1]; -} _PyCodeObjectExtra; -/*[clinic input] -class code "PyCodeObject *" "&PyCode_Type" -[clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=78aa5d576683bb4b]*/ +/****************** + * generic helpers + ******************/ /* all_name_chars(s): true iff s matches [a-zA-Z0-9_]* */ static int @@ -113,6 +107,53 @@ intern_string_constants(PyObject *tuple, int *modified) return 0; } +/* Return a shallow copy of a tuple that is + guaranteed to contain exact strings, by converting string subclasses + to exact strings and complaining if a non-string is found. */ +static PyObject* +validate_and_copy_tuple(PyObject *tup) +{ + PyObject *newtuple; + PyObject *item; + Py_ssize_t i, len; + + len = PyTuple_GET_SIZE(tup); + newtuple = PyTuple_New(len); + if (newtuple == NULL) + return NULL; + + for (i = 0; i < len; i++) { + item = PyTuple_GET_ITEM(tup, i); + if (PyUnicode_CheckExact(item)) { + Py_INCREF(item); + } + else if (!PyUnicode_Check(item)) { + PyErr_Format( + PyExc_TypeError, + "name tuples must contain only " + "strings, not '%.500s'", + Py_TYPE(item)->tp_name); + Py_DECREF(newtuple); + return NULL; + } + else { + item = _PyUnicode_Copy(item); + if (item == NULL) { + Py_DECREF(newtuple); + return NULL; + } + } + PyTuple_SET_ITEM(newtuple, i, item); + } + + return newtuple; +} + + +/****************** + * the "constructors" + ******************/ + PyCodeObject * PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount, int nlocals, int stacksize, int flags, @@ -288,49 +329,6 @@ PyCode_New(int argcount, int kwonlyargcount, name, firstlineno, linetable, exceptiontable); } -int -_PyCode_InitOpcache(PyCodeObject *co) -{ - Py_ssize_t co_size = PyBytes_Size(co->co_code) / sizeof(_Py_CODEUNIT); - co->co_opcache_map = (unsigned char *)PyMem_Calloc(co_size, 1); - if (co->co_opcache_map == NULL) { - return -1; - } - - _Py_CODEUNIT *opcodes = (_Py_CODEUNIT*)PyBytes_AS_STRING(co->co_code); - Py_ssize_t opts = 0; - - for (Py_ssize_t i = 0; i < co_size;) { - unsigned char opcode = _Py_OPCODE(opcodes[i]); - i++; // 'i' is now aligned to (next_instr - first_instr) - - // TODO: LOAD_METHOD - if (opcode == LOAD_GLOBAL || opcode == LOAD_ATTR) { - opts++; - co->co_opcache_map[i] = (unsigned char)opts; - if (opts > 254) { - break; - } - } - } - - if (opts) { - co->co_opcache = (_PyOpcache *)PyMem_Calloc(opts, sizeof(_PyOpcache)); - if (co->co_opcache == NULL) { - PyMem_Free(co->co_opcache_map); - return -1; - } - } - else { - PyMem_Free(co->co_opcache_map); - co->co_opcache_map = NULL; - co->co_opcache = NULL; - } - - co->co_opcache_size = (unsigned char)opts; - return 0; -} - PyCodeObject * PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) { @@ -382,28 +380,134 @@ failed: return result; } -#define OFF(x) offsetof(PyCodeObject, x) -static PyMemberDef code_memberlist[] = { - {"co_argcount", T_INT, OFF(co_argcount), READONLY}, - {"co_posonlyargcount", T_INT, OFF(co_posonlyargcount), READONLY}, - {"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY}, - {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, - {"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, - {"co_flags", T_INT, OFF(co_flags), READONLY}, - {"co_code", T_OBJECT, OFF(co_code), READONLY}, - {"co_consts", T_OBJECT, OFF(co_consts), READONLY}, - {"co_names", T_OBJECT, OFF(co_names), READONLY}, - {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY}, - {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY}, - {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, - {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, - {"co_name", T_OBJECT, OFF(co_name), READONLY}, - {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, - {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, - {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, - {NULL} /* Sentinel */ -}; +/****************** + * the line table (co_linetable) + ******************/ + +/* Use co_linetable to compute the line number from a bytecode index, addrq. See + lnotab_notes.txt for the details of the lnotab representation. +*/ + +int +PyCode_Addr2Line(PyCodeObject *co, int addrq) +{ + if (addrq < 0) { + return co->co_firstlineno; + } + assert(addrq >= 0 && addrq < PyBytes_GET_SIZE(co->co_code)); + PyCodeAddressRange bounds; + _PyCode_InitAddressRange(co, &bounds); + return _PyCode_CheckLineNumber(addrq, &bounds); +} + +void +PyLineTable_InitAddressRange(char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) +{ + range->opaque.lo_next = linetable; + range->opaque.limit = range->opaque.lo_next + length; + range->ar_start = -1; + range->ar_end = 0; + range->opaque.computed_line = firstlineno; + range->ar_line = -1; +} + +int +_PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) +{ + char *linetable = PyBytes_AS_STRING(co->co_linetable); + Py_ssize_t length = PyBytes_GET_SIZE(co->co_linetable); + PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); + return bounds->ar_line; +} + +/* Update *bounds to describe the first and one-past-the-last instructions in + the same line as lasti. Return the number of that line, or -1 if lasti is out of bounds. */ +int +_PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds) +{ + while (bounds->ar_end <= lasti) { + if (!PyLineTable_NextAddressRange(bounds)) { + return -1; + } + } + while (bounds->ar_start > lasti) { + if (!PyLineTable_PreviousAddressRange(bounds)) { + return -1; + } + } + return bounds->ar_line; +} + +static void +retreat(PyCodeAddressRange *bounds) +{ + int ldelta = ((signed char *)bounds->opaque.lo_next)[-1]; + if (ldelta == -128) { + ldelta = 0; + } + bounds->opaque.computed_line -= ldelta; + bounds->opaque.lo_next -= 2; + bounds->ar_end = bounds->ar_start; + bounds->ar_start -= ((unsigned char *)bounds->opaque.lo_next)[-2]; + ldelta = ((signed char *)bounds->opaque.lo_next)[-1]; + if (ldelta == -128) { + bounds->ar_line = -1; + } + else { + bounds->ar_line = bounds->opaque.computed_line; + } +} + +static void +advance(PyCodeAddressRange *bounds) +{ + bounds->ar_start = bounds->ar_end; + int delta = ((unsigned char *)bounds->opaque.lo_next)[0]; + bounds->ar_end += delta; + int ldelta = ((signed char *)bounds->opaque.lo_next)[1]; + bounds->opaque.lo_next += 2; + if (ldelta == -128) { + bounds->ar_line = -1; + } + else { + bounds->opaque.computed_line += ldelta; + bounds->ar_line = bounds->opaque.computed_line; + } +} + +static inline int +at_end(PyCodeAddressRange *bounds) { + return bounds->opaque.lo_next >= bounds->opaque.limit; +} + +int +PyLineTable_PreviousAddressRange(PyCodeAddressRange *range) +{ + if (range->ar_start <= 0) { + return 0; + } + retreat(range); + while (range->ar_start == range->ar_end) { + assert(range->ar_start > 0); + retreat(range); + } + return 1; +} + +int +PyLineTable_NextAddressRange(PyCodeAddressRange *range) +{ + if (at_end(range)) { + return 0; + } + advance(range); + while (range->ar_start == range->ar_end) { + assert(!at_end(range)); + advance(range); + } + return 1; +} static int emit_pair(PyObject **bytes, int *offset, int a, int b) @@ -448,7 +552,7 @@ emit_delta(PyObject **bytes, int bdelta, int ldelta, int *offset) } static PyObject * -code_getlnotab(PyCodeObject *code, void *closure) +decode_linetable(PyCodeObject *code) { PyCodeAddressRange bounds; PyObject *bytes; @@ -477,54 +581,244 @@ code_getlnotab(PyCodeObject *code, void *closure) } -static PyGetSetDef code_getsetlist[] = { - {"co_lnotab", (getter)code_getlnotab, NULL, NULL}, - {0} +typedef struct { + PyObject_HEAD + PyCodeObject *li_code; + PyCodeAddressRange li_line; + char *li_end; +} lineiterator; + + +static void +lineiter_dealloc(lineiterator *li) +{ + Py_DECREF(li->li_code); + Py_TYPE(li)->tp_free(li); +} + +static PyObject * +lineiter_next(lineiterator *li) +{ + PyCodeAddressRange *bounds = &li->li_line; + if (!PyLineTable_NextAddressRange(bounds)) { + return NULL; + } + PyObject *start = NULL; + PyObject *end = NULL; + PyObject *line = NULL; + PyObject *result = PyTuple_New(3); + start = PyLong_FromLong(bounds->ar_start); + end = PyLong_FromLong(bounds->ar_end); + if (bounds->ar_line < 0) { + Py_INCREF(Py_None); + line = Py_None; + } + else { + line = PyLong_FromLong(bounds->ar_line); + } + if (result == NULL || start == NULL || end == NULL || line == NULL) { + goto error; + } + PyTuple_SET_ITEM(result, 0, start); + PyTuple_SET_ITEM(result, 1, end); + PyTuple_SET_ITEM(result, 2, line); + return result; +error: + Py_XDECREF(start); + Py_XDECREF(end); + Py_XDECREF(line); + Py_XDECREF(result); + return result; +} + +static PyTypeObject LineIterator = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "line_iterator", /* tp_name */ + sizeof(lineiterator), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)lineiter_dealloc, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)lineiter_next, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + PyObject_Del, /* tp_free */ }; - -/* Helper for code_new: return a shallow copy of a tuple that is - guaranteed to contain exact strings, by converting string subclasses - to exact strings and complaining if a non-string is found. */ -static PyObject* -validate_and_copy_tuple(PyObject *tup) +static lineiterator * +new_linesiterator(PyCodeObject *code) { - PyObject *newtuple; - PyObject *item; - Py_ssize_t i, len; - - len = PyTuple_GET_SIZE(tup); - newtuple = PyTuple_New(len); - if (newtuple == NULL) + lineiterator *li = (lineiterator *)PyType_GenericAlloc(&LineIterator, 0); + if (li == NULL) { return NULL; + } + Py_INCREF(code); + li->li_code = code; + _PyCode_InitAddressRange(code, &li->li_line); + return li; +} - for (i = 0; i < len; i++) { - item = PyTuple_GET_ITEM(tup, i); - if (PyUnicode_CheckExact(item)) { - Py_INCREF(item); - } - else if (!PyUnicode_Check(item)) { - PyErr_Format( - PyExc_TypeError, - "name tuples must contain only " - "strings, not '%.500s'", - Py_TYPE(item)->tp_name); - Py_DECREF(newtuple); - return NULL; - } - else { - item = _PyUnicode_Copy(item); - if (item == NULL) { - Py_DECREF(newtuple); - return NULL; - } - } - PyTuple_SET_ITEM(newtuple, i, item); + +/****************** + * the opcache + ******************/ + +int +_PyCode_InitOpcache(PyCodeObject *co) +{ + Py_ssize_t co_size = PyBytes_Size(co->co_code) / sizeof(_Py_CODEUNIT); + co->co_opcache_map = (unsigned char *)PyMem_Calloc(co_size, 1); + if (co->co_opcache_map == NULL) { + return -1; } - return newtuple; + _Py_CODEUNIT *opcodes = (_Py_CODEUNIT*)PyBytes_AS_STRING(co->co_code); + Py_ssize_t opts = 0; + + for (Py_ssize_t i = 0; i < co_size;) { + unsigned char opcode = _Py_OPCODE(opcodes[i]); + i++; // 'i' is now aligned to (next_instr - first_instr) + + // TODO: LOAD_METHOD + if (opcode == LOAD_GLOBAL || opcode == LOAD_ATTR) { + opts++; + co->co_opcache_map[i] = (unsigned char)opts; + if (opts > 254) { + break; + } + } + } + + if (opts) { + co->co_opcache = (_PyOpcache *)PyMem_Calloc(opts, sizeof(_PyOpcache)); + if (co->co_opcache == NULL) { + PyMem_Free(co->co_opcache_map); + return -1; + } + } + else { + PyMem_Free(co->co_opcache_map); + co->co_opcache_map = NULL; + co->co_opcache = NULL; + } + + co->co_opcache_size = (unsigned char)opts; + return 0; } + +/****************** + * "extra" frame eval info (see PEP 523) + ******************/ + +/* Holder for co_extra information */ +typedef struct { + Py_ssize_t ce_size; + void *ce_extras[1]; +} _PyCodeObjectExtra; + + +int +_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) +{ + if (!PyCode_Check(code)) { + PyErr_BadInternalCall(); + return -1; + } + + PyCodeObject *o = (PyCodeObject*) code; + _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra*) o->co_extra; + + if (co_extra == NULL || co_extra->ce_size <= index) { + *extra = NULL; + return 0; + } + + *extra = co_extra->ce_extras[index]; + return 0; +} + + +int +_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + + if (!PyCode_Check(code) || index < 0 || + index >= interp->co_extra_user_count) { + PyErr_BadInternalCall(); + return -1; + } + + PyCodeObject *o = (PyCodeObject*) code; + _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra *) o->co_extra; + + if (co_extra == NULL || co_extra->ce_size <= index) { + Py_ssize_t i = (co_extra == NULL ? 0 : co_extra->ce_size); + co_extra = PyMem_Realloc( + co_extra, + sizeof(_PyCodeObjectExtra) + + (interp->co_extra_user_count-1) * sizeof(void*)); + if (co_extra == NULL) { + return -1; + } + for (; i < interp->co_extra_user_count; i++) { + co_extra->ce_extras[i] = NULL; + } + co_extra->ce_size = interp->co_extra_user_count; + o->co_extra = co_extra; + } + + if (co_extra->ce_extras[index] != NULL) { + freefunc free = interp->co_extra_freefuncs[index]; + if (free != NULL) { + free(co_extra->ce_extras[index]); + } + } + + co_extra->ce_extras[index] = extra; + return 0; +} + + +/****************** + * PyCode_Type + ******************/ + +/*[clinic input] +class code "PyCodeObject *" "&PyCode_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=78aa5d576683bb4b]*/ + /*[clinic input] @classmethod code.__new__ as code_new @@ -681,6 +975,164 @@ code_dealloc(PyCodeObject *co) PyObject_Free(co); } +static PyObject * +code_repr(PyCodeObject *co) +{ + int lineno; + if (co->co_firstlineno != 0) + lineno = co->co_firstlineno; + else + lineno = -1; + if (co->co_filename && PyUnicode_Check(co->co_filename)) { + return PyUnicode_FromFormat( + "", + co->co_name, co, co->co_filename, lineno); + } else { + return PyUnicode_FromFormat( + "", + co->co_name, co, lineno); + } +} + +static PyObject * +code_richcompare(PyObject *self, PyObject *other, int op) +{ + PyCodeObject *co, *cp; + int eq; + PyObject *consts1, *consts2; + PyObject *res; + + if ((op != Py_EQ && op != Py_NE) || + !PyCode_Check(self) || + !PyCode_Check(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + co = (PyCodeObject *)self; + cp = (PyCodeObject *)other; + + eq = PyObject_RichCompareBool(co->co_name, cp->co_name, Py_EQ); + if (!eq) goto unequal; + eq = co->co_argcount == cp->co_argcount; + if (!eq) goto unequal; + eq = co->co_posonlyargcount == cp->co_posonlyargcount; + if (!eq) goto unequal; + eq = co->co_kwonlyargcount == cp->co_kwonlyargcount; + if (!eq) goto unequal; + eq = co->co_nlocals == cp->co_nlocals; + if (!eq) goto unequal; + eq = co->co_flags == cp->co_flags; + if (!eq) goto unequal; + eq = co->co_firstlineno == cp->co_firstlineno; + if (!eq) goto unequal; + eq = PyObject_RichCompareBool(co->co_code, cp->co_code, Py_EQ); + if (eq <= 0) goto unequal; + + /* compare constants */ + consts1 = _PyCode_ConstantKey(co->co_consts); + if (!consts1) + return NULL; + consts2 = _PyCode_ConstantKey(cp->co_consts); + if (!consts2) { + Py_DECREF(consts1); + return NULL; + } + eq = PyObject_RichCompareBool(consts1, consts2, Py_EQ); + Py_DECREF(consts1); + Py_DECREF(consts2); + if (eq <= 0) goto unequal; + + eq = PyObject_RichCompareBool(co->co_names, cp->co_names, Py_EQ); + if (eq <= 0) goto unequal; + eq = PyObject_RichCompareBool(co->co_varnames, cp->co_varnames, Py_EQ); + if (eq <= 0) goto unequal; + eq = PyObject_RichCompareBool(co->co_freevars, cp->co_freevars, Py_EQ); + if (eq <= 0) goto unequal; + eq = PyObject_RichCompareBool(co->co_cellvars, cp->co_cellvars, Py_EQ); + if (eq <= 0) goto unequal; + + if (op == Py_EQ) + res = Py_True; + else + res = Py_False; + goto done; + + unequal: + if (eq < 0) + return NULL; + if (op == Py_NE) + res = Py_True; + else + res = Py_False; + + done: + Py_INCREF(res); + return res; +} + +static Py_hash_t +code_hash(PyCodeObject *co) +{ + Py_hash_t h, h0, h1, h2, h3, h4, h5, h6; + h0 = PyObject_Hash(co->co_name); + if (h0 == -1) return -1; + h1 = PyObject_Hash(co->co_code); + if (h1 == -1) return -1; + h2 = PyObject_Hash(co->co_consts); + if (h2 == -1) return -1; + h3 = PyObject_Hash(co->co_names); + if (h3 == -1) return -1; + h4 = PyObject_Hash(co->co_varnames); + if (h4 == -1) return -1; + h5 = PyObject_Hash(co->co_freevars); + if (h5 == -1) return -1; + h6 = PyObject_Hash(co->co_cellvars); + if (h6 == -1) return -1; + h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ + co->co_argcount ^ co->co_posonlyargcount ^ co->co_kwonlyargcount ^ + co->co_nlocals ^ co->co_flags; + if (h == -1) h = -2; + return h; +} + + +#define OFF(x) offsetof(PyCodeObject, x) + +static PyMemberDef code_memberlist[] = { + {"co_argcount", T_INT, OFF(co_argcount), READONLY}, + {"co_posonlyargcount", T_INT, OFF(co_posonlyargcount), READONLY}, + {"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY}, + {"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, + {"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, + {"co_flags", T_INT, OFF(co_flags), READONLY}, + {"co_code", T_OBJECT, OFF(co_code), READONLY}, + {"co_consts", T_OBJECT, OFF(co_consts), READONLY}, + {"co_names", T_OBJECT, OFF(co_names), READONLY}, + {"co_varnames", T_OBJECT, OFF(co_varnames), READONLY}, + {"co_freevars", T_OBJECT, OFF(co_freevars), READONLY}, + {"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, + {"co_filename", T_OBJECT, OFF(co_filename), READONLY}, + {"co_name", T_OBJECT, OFF(co_name), READONLY}, + {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, + {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY}, + {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY}, + {NULL} /* Sentinel */ +}; + + + +static PyObject * +code_getlnotab(PyCodeObject *code, void *closure) +{ + return decode_linetable(code); +} + +static PyGetSetDef code_getsetlist[] = { + {"co_lnotab", (getter)code_getlnotab, NULL, NULL}, + {0} +}; + + static PyObject * code_sizeof(PyCodeObject *co, PyObject *Py_UNUSED(args)) { @@ -704,6 +1156,12 @@ code_sizeof(PyCodeObject *co, PyObject *Py_UNUSED(args)) return PyLong_FromSsize_t(res); } +static PyObject * +code_linesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args)) +{ + return (PyObject *)new_linesiterator(code); +} + /*[clinic input] code.replace @@ -772,24 +1230,61 @@ code_replace_impl(PyCodeObject *self, int co_argcount, co_firstlineno, (PyObject*)co_linetable, (PyObject*)co_exceptiontable); } -static PyObject * -code_repr(PyCodeObject *co) -{ - int lineno; - if (co->co_firstlineno != 0) - lineno = co->co_firstlineno; - else - lineno = -1; - if (co->co_filename && PyUnicode_Check(co->co_filename)) { - return PyUnicode_FromFormat( - "", - co->co_name, co, co->co_filename, lineno); - } else { - return PyUnicode_FromFormat( - "", - co->co_name, co, lineno); - } -} +/* XXX code objects need to participate in GC? */ + +static struct PyMethodDef code_methods[] = { + {"__sizeof__", (PyCFunction)code_sizeof, METH_NOARGS}, + {"co_lines", (PyCFunction)code_linesiterator, METH_NOARGS}, + CODE_REPLACE_METHODDEF + {NULL, NULL} /* sentinel */ +}; + + +PyTypeObject PyCode_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "code", + sizeof(PyCodeObject), + 0, + (destructor)code_dealloc, /* tp_dealloc */ + 0, /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + (reprfunc)code_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)code_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + code_new__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + code_richcompare, /* tp_richcompare */ + offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + code_methods, /* tp_methods */ + code_memberlist, /* tp_members */ + code_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + code_new, /* tp_new */ +}; + + +/****************** + * other API + ******************/ PyObject* _PyCode_ConstantKey(PyObject *op) @@ -920,448 +1415,3 @@ _PyCode_ConstantKey(PyObject *op) } return key; } - -static PyObject * -code_richcompare(PyObject *self, PyObject *other, int op) -{ - PyCodeObject *co, *cp; - int eq; - PyObject *consts1, *consts2; - PyObject *res; - - if ((op != Py_EQ && op != Py_NE) || - !PyCode_Check(self) || - !PyCode_Check(other)) { - Py_RETURN_NOTIMPLEMENTED; - } - - co = (PyCodeObject *)self; - cp = (PyCodeObject *)other; - - eq = PyObject_RichCompareBool(co->co_name, cp->co_name, Py_EQ); - if (!eq) goto unequal; - eq = co->co_argcount == cp->co_argcount; - if (!eq) goto unequal; - eq = co->co_posonlyargcount == cp->co_posonlyargcount; - if (!eq) goto unequal; - eq = co->co_kwonlyargcount == cp->co_kwonlyargcount; - if (!eq) goto unequal; - eq = co->co_nlocals == cp->co_nlocals; - if (!eq) goto unequal; - eq = co->co_flags == cp->co_flags; - if (!eq) goto unequal; - eq = co->co_firstlineno == cp->co_firstlineno; - if (!eq) goto unequal; - eq = PyObject_RichCompareBool(co->co_code, cp->co_code, Py_EQ); - if (eq <= 0) goto unequal; - - /* compare constants */ - consts1 = _PyCode_ConstantKey(co->co_consts); - if (!consts1) - return NULL; - consts2 = _PyCode_ConstantKey(cp->co_consts); - if (!consts2) { - Py_DECREF(consts1); - return NULL; - } - eq = PyObject_RichCompareBool(consts1, consts2, Py_EQ); - Py_DECREF(consts1); - Py_DECREF(consts2); - if (eq <= 0) goto unequal; - - eq = PyObject_RichCompareBool(co->co_names, cp->co_names, Py_EQ); - if (eq <= 0) goto unequal; - eq = PyObject_RichCompareBool(co->co_varnames, cp->co_varnames, Py_EQ); - if (eq <= 0) goto unequal; - eq = PyObject_RichCompareBool(co->co_freevars, cp->co_freevars, Py_EQ); - if (eq <= 0) goto unequal; - eq = PyObject_RichCompareBool(co->co_cellvars, cp->co_cellvars, Py_EQ); - if (eq <= 0) goto unequal; - - if (op == Py_EQ) - res = Py_True; - else - res = Py_False; - goto done; - - unequal: - if (eq < 0) - return NULL; - if (op == Py_NE) - res = Py_True; - else - res = Py_False; - - done: - Py_INCREF(res); - return res; -} - -static Py_hash_t -code_hash(PyCodeObject *co) -{ - Py_hash_t h, h0, h1, h2, h3, h4, h5, h6; - h0 = PyObject_Hash(co->co_name); - if (h0 == -1) return -1; - h1 = PyObject_Hash(co->co_code); - if (h1 == -1) return -1; - h2 = PyObject_Hash(co->co_consts); - if (h2 == -1) return -1; - h3 = PyObject_Hash(co->co_names); - if (h3 == -1) return -1; - h4 = PyObject_Hash(co->co_varnames); - if (h4 == -1) return -1; - h5 = PyObject_Hash(co->co_freevars); - if (h5 == -1) return -1; - h6 = PyObject_Hash(co->co_cellvars); - if (h6 == -1) return -1; - h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ - co->co_argcount ^ co->co_posonlyargcount ^ co->co_kwonlyargcount ^ - co->co_nlocals ^ co->co_flags; - if (h == -1) h = -2; - return h; -} - -typedef struct { - PyObject_HEAD - PyCodeObject *li_code; - PyCodeAddressRange li_line; - char *li_end; -} lineiterator; - - -static void -lineiter_dealloc(lineiterator *li) -{ - Py_DECREF(li->li_code); - Py_TYPE(li)->tp_free(li); -} - -static PyObject * -lineiter_next(lineiterator *li) -{ - PyCodeAddressRange *bounds = &li->li_line; - if (!PyLineTable_NextAddressRange(bounds)) { - return NULL; - } - PyObject *start = NULL; - PyObject *end = NULL; - PyObject *line = NULL; - PyObject *result = PyTuple_New(3); - start = PyLong_FromLong(bounds->ar_start); - end = PyLong_FromLong(bounds->ar_end); - if (bounds->ar_line < 0) { - Py_INCREF(Py_None); - line = Py_None; - } - else { - line = PyLong_FromLong(bounds->ar_line); - } - if (result == NULL || start == NULL || end == NULL || line == NULL) { - goto error; - } - PyTuple_SET_ITEM(result, 0, start); - PyTuple_SET_ITEM(result, 1, end); - PyTuple_SET_ITEM(result, 2, line); - return result; -error: - Py_XDECREF(start); - Py_XDECREF(end); - Py_XDECREF(line); - Py_XDECREF(result); - return result; -} - -static PyTypeObject LineIterator = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "line_iterator", /* tp_name */ - sizeof(lineiterator), /* tp_basicsize */ - 0, /* tp_itemsize */ - /* methods */ - (destructor)lineiter_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - PyObject_SelfIter, /* tp_iter */ - (iternextfunc)lineiter_next, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - PyObject_Del, /* tp_free */ -}; - -static PyObject * -code_linesiterator(PyCodeObject *code, PyObject *Py_UNUSED(args)) -{ - lineiterator *li = (lineiterator *)PyType_GenericAlloc(&LineIterator, 0); - if (li == NULL) { - return NULL; - } - Py_INCREF(code); - li->li_code = code; - _PyCode_InitAddressRange(code, &li->li_line); - return (PyObject *)li; -} - -static void -retreat(PyCodeAddressRange *bounds) -{ - int ldelta = ((signed char *)bounds->opaque.lo_next)[-1]; - if (ldelta == -128) { - ldelta = 0; - } - bounds->opaque.computed_line -= ldelta; - bounds->opaque.lo_next -= 2; - bounds->ar_end = bounds->ar_start; - bounds->ar_start -= ((unsigned char *)bounds->opaque.lo_next)[-2]; - ldelta = ((signed char *)bounds->opaque.lo_next)[-1]; - if (ldelta == -128) { - bounds->ar_line = -1; - } - else { - bounds->ar_line = bounds->opaque.computed_line; - } -} - -static void -advance(PyCodeAddressRange *bounds) -{ - bounds->ar_start = bounds->ar_end; - int delta = ((unsigned char *)bounds->opaque.lo_next)[0]; - bounds->ar_end += delta; - int ldelta = ((signed char *)bounds->opaque.lo_next)[1]; - bounds->opaque.lo_next += 2; - if (ldelta == -128) { - bounds->ar_line = -1; - } - else { - bounds->opaque.computed_line += ldelta; - bounds->ar_line = bounds->opaque.computed_line; - } -} - -static inline int -at_end(PyCodeAddressRange *bounds) { - return bounds->opaque.lo_next >= bounds->opaque.limit; -} - -int -PyLineTable_PreviousAddressRange(PyCodeAddressRange *range) -{ - if (range->ar_start <= 0) { - return 0; - } - retreat(range); - while (range->ar_start == range->ar_end) { - assert(range->ar_start > 0); - retreat(range); - } - return 1; -} - -int -PyLineTable_NextAddressRange(PyCodeAddressRange *range) -{ - if (at_end(range)) { - return 0; - } - advance(range); - while (range->ar_start == range->ar_end) { - assert(!at_end(range)); - advance(range); - } - return 1; -} - - -/* XXX code objects need to participate in GC? */ - -static struct PyMethodDef code_methods[] = { - {"__sizeof__", (PyCFunction)code_sizeof, METH_NOARGS}, - {"co_lines", (PyCFunction)code_linesiterator, METH_NOARGS}, - CODE_REPLACE_METHODDEF - {NULL, NULL} /* sentinel */ -}; - -PyTypeObject PyCode_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - "code", - sizeof(PyCodeObject), - 0, - (destructor)code_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)code_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)code_hash, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - code_new__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - code_richcompare, /* tp_richcompare */ - offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - code_methods, /* tp_methods */ - code_memberlist, /* tp_members */ - code_getsetlist, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - code_new, /* tp_new */ -}; - -/* Use co_linetable to compute the line number from a bytecode index, addrq. See - lnotab_notes.txt for the details of the lnotab representation. -*/ - -int -PyCode_Addr2Line(PyCodeObject *co, int addrq) -{ - if (addrq < 0) { - return co->co_firstlineno; - } - assert(addrq >= 0 && addrq < PyBytes_GET_SIZE(co->co_code)); - PyCodeAddressRange bounds; - _PyCode_InitAddressRange(co, &bounds); - return _PyCode_CheckLineNumber(addrq, &bounds); -} - -void -PyLineTable_InitAddressRange(char *linetable, Py_ssize_t length, int firstlineno, PyCodeAddressRange *range) -{ - range->opaque.lo_next = linetable; - range->opaque.limit = range->opaque.lo_next + length; - range->ar_start = -1; - range->ar_end = 0; - range->opaque.computed_line = firstlineno; - range->ar_line = -1; -} - -int -_PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds) -{ - char *linetable = PyBytes_AS_STRING(co->co_linetable); - Py_ssize_t length = PyBytes_GET_SIZE(co->co_linetable); - PyLineTable_InitAddressRange(linetable, length, co->co_firstlineno, bounds); - return bounds->ar_line; -} - -/* Update *bounds to describe the first and one-past-the-last instructions in - the same line as lasti. Return the number of that line, or -1 if lasti is out of bounds. */ -int -_PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds) -{ - while (bounds->ar_end <= lasti) { - if (!PyLineTable_NextAddressRange(bounds)) { - return -1; - } - } - while (bounds->ar_start > lasti) { - if (!PyLineTable_PreviousAddressRange(bounds)) { - return -1; - } - } - return bounds->ar_line; -} - - -int -_PyCode_GetExtra(PyObject *code, Py_ssize_t index, void **extra) -{ - if (!PyCode_Check(code)) { - PyErr_BadInternalCall(); - return -1; - } - - PyCodeObject *o = (PyCodeObject*) code; - _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra*) o->co_extra; - - if (co_extra == NULL || co_extra->ce_size <= index) { - *extra = NULL; - return 0; - } - - *extra = co_extra->ce_extras[index]; - return 0; -} - - -int -_PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra) -{ - PyInterpreterState *interp = _PyInterpreterState_GET(); - - if (!PyCode_Check(code) || index < 0 || - index >= interp->co_extra_user_count) { - PyErr_BadInternalCall(); - return -1; - } - - PyCodeObject *o = (PyCodeObject*) code; - _PyCodeObjectExtra *co_extra = (_PyCodeObjectExtra *) o->co_extra; - - if (co_extra == NULL || co_extra->ce_size <= index) { - Py_ssize_t i = (co_extra == NULL ? 0 : co_extra->ce_size); - co_extra = PyMem_Realloc( - co_extra, - sizeof(_PyCodeObjectExtra) + - (interp->co_extra_user_count-1) * sizeof(void*)); - if (co_extra == NULL) { - return -1; - } - for (; i < interp->co_extra_user_count; i++) { - co_extra->ce_extras[i] = NULL; - } - co_extra->ce_size = interp->co_extra_user_count; - o->co_extra = co_extra; - } - - if (co_extra->ce_extras[index] != NULL) { - freefunc free = interp->co_extra_freefuncs[index]; - if (free != NULL) { - free(co_extra->ce_extras[index]); - } - } - - co_extra->ce_extras[index] = extra; - return 0; -}