bpo-42246: Partial implementation of PEP 626. (GH-23113)

* Implement new line number table format, as defined in PEP 626.
This commit is contained in:
Mark Shannon 2020-11-12 09:43:29 +00:00 committed by GitHub
parent cda99b4022
commit 877df851c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 5364 additions and 5000 deletions

View file

@ -119,7 +119,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars,
PyObject *filename, PyObject *name, int firstlineno,
PyObject *lnotab)
PyObject *linetable)
{
PyCodeObject *co;
Py_ssize_t *cell2arg = NULL;
@ -137,7 +137,7 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
cellvars == NULL || !PyTuple_Check(cellvars) ||
name == NULL || !PyUnicode_Check(name) ||
filename == NULL || !PyUnicode_Check(filename) ||
lnotab == NULL || !PyBytes_Check(lnotab)) {
linetable == NULL || !PyBytes_Check(linetable)) {
PyErr_BadInternalCall();
return NULL;
}
@ -258,8 +258,8 @@ PyCode_NewWithPosOnlyArgs(int argcount, int posonlyargcount, int kwonlyargcount,
Py_INCREF(name);
co->co_name = name;
co->co_firstlineno = firstlineno;
Py_INCREF(lnotab);
co->co_lnotab = lnotab;
Py_INCREF(linetable);
co->co_linetable = linetable;
co->co_zombieframe = NULL;
co->co_weakreflist = NULL;
co->co_extra = NULL;
@ -277,12 +277,12 @@ PyCode_New(int argcount, int kwonlyargcount,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *freevars, PyObject *cellvars,
PyObject *filename, PyObject *name, int firstlineno,
PyObject *lnotab)
PyObject *linetable)
{
return PyCode_NewWithPosOnlyArgs(argcount, 0, kwonlyargcount, nlocals,
stacksize, flags, code, consts, names,
varnames, freevars, cellvars, filename,
name, firstlineno, lnotab);
name, firstlineno, linetable);
}
int
@ -369,7 +369,7 @@ PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
filename_ob, /* filename */
funcname_ob, /* name */
firstlineno, /* firstlineno */
emptystring /* lnotab */
emptystring /* linetable */
);
failed:
@ -395,11 +395,89 @@ static PyMemberDef code_memberlist[] = {
{"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_lnotab", T_OBJECT, OFF(co_lnotab), READONLY},
{"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY},
{"co_linetable", T_OBJECT, OFF(co_linetable), READONLY},
{NULL} /* Sentinel */
};
static int
emit_pair(PyObject **bytes, int *offset, int a, int b)
{
Py_ssize_t len = PyBytes_GET_SIZE(*bytes);
if (*offset + 2 >= len) {
if (_PyBytes_Resize(bytes, len * 2) < 0)
return 0;
}
unsigned char *lnotab = (unsigned char *)
PyBytes_AS_STRING(*bytes) + *offset;
*lnotab++ = a;
*lnotab++ = b;
*offset += 2;
return 1;
}
static int
emit_delta(PyObject **bytes, int bdelta, int ldelta, int *offset)
{
while (bdelta > 255) {
if (!emit_pair(bytes, offset, 255, 0)) {
return 0;
}
bdelta -= 255;
}
while (ldelta > 127) {
if (!emit_pair(bytes, offset, bdelta, 127)) {
return 0;
}
bdelta = 0;
ldelta -= 127;
}
while (ldelta < -128) {
if (!emit_pair(bytes, offset, bdelta, -128)) {
return 0;
}
bdelta = 0;
ldelta += 128;
}
return emit_pair(bytes, offset, bdelta, ldelta);
}
static PyObject *
code_getlnotab(PyCodeObject *code, void *closure)
{
PyCodeAddressRange bounds;
PyObject *bytes;
int table_offset = 0;
int code_offset = 0;
int line = code->co_firstlineno;
bytes = PyBytes_FromStringAndSize(NULL, 64);
if (bytes == NULL) {
return NULL;
}
_PyCode_InitAddressRange(code, &bounds);
while (PyLineTable_NextAddressRange(&bounds)) {
if (bounds.ar_computed_line != line) {
int bdelta = bounds.ar_start - code_offset;
int ldelta = bounds.ar_computed_line - line;
if (!emit_delta(&bytes, bdelta, ldelta, &table_offset)) {
Py_DECREF(bytes);
return NULL;
}
code_offset = bounds.ar_start;
line = bounds.ar_computed_line;
}
}
_PyBytes_Resize(&bytes, table_offset);
return bytes;
}
static PyGetSetDef code_getsetlist[] = {
{"co_lnotab", (getter)code_getlnotab, NULL, NULL},
{0}
};
/* 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. */
@ -459,7 +537,7 @@ code.__new__ as code_new
filename: unicode
name: unicode
firstlineno: int
lnotab: object(subclass_of="&PyBytes_Type")
linetable: object(subclass_of="&PyBytes_Type")
freevars: object(subclass_of="&PyTuple_Type", c_default="NULL") = ()
cellvars: object(subclass_of="&PyTuple_Type", c_default="NULL") = ()
/
@ -472,9 +550,9 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
int kwonlyargcount, int nlocals, int stacksize, int flags,
PyObject *code, PyObject *consts, PyObject *names,
PyObject *varnames, PyObject *filename, PyObject *name,
int firstlineno, PyObject *lnotab, PyObject *freevars,
int firstlineno, PyObject *linetable, PyObject *freevars,
PyObject *cellvars)
/*[clinic end generated code: output=612aac5395830184 input=85e678ea4178f234]*/
/*[clinic end generated code: output=42c1839b082ba293 input=0ec80da632b99f57]*/
{
PyObject *co = NULL;
PyObject *ournames = NULL;
@ -540,7 +618,7 @@ code_new_impl(PyTypeObject *type, int argcount, int posonlyargcount,
code, consts, ournames,
ourvarnames, ourfreevars,
ourcellvars, filename,
name, firstlineno, lnotab);
name, firstlineno, linetable);
cleanup:
Py_XDECREF(ournames);
Py_XDECREF(ourvarnames);
@ -584,7 +662,7 @@ code_dealloc(PyCodeObject *co)
Py_XDECREF(co->co_cellvars);
Py_XDECREF(co->co_filename);
Py_XDECREF(co->co_name);
Py_XDECREF(co->co_lnotab);
Py_XDECREF(co->co_linetable);
if (co->co_cell2arg != NULL)
PyMem_FREE(co->co_cell2arg);
if (co->co_zombieframe != NULL)
@ -636,7 +714,7 @@ code.replace
co_cellvars: object(subclass_of="&PyTuple_Type", c_default="self->co_cellvars") = None
co_filename: unicode(c_default="self->co_filename") = None
co_name: unicode(c_default="self->co_name") = None
co_lnotab: PyBytesObject(c_default="(PyBytesObject *)self->co_lnotab") = None
co_linetable: PyBytesObject(c_default="(PyBytesObject *)self->co_linetable") = None
Return a copy of the code object with new values for the specified fields.
[clinic start generated code]*/
@ -649,8 +727,8 @@ code_replace_impl(PyCodeObject *self, int co_argcount,
PyObject *co_consts, PyObject *co_names,
PyObject *co_varnames, PyObject *co_freevars,
PyObject *co_cellvars, PyObject *co_filename,
PyObject *co_name, PyBytesObject *co_lnotab)
/*[clinic end generated code: output=25c8e303913bcace input=d9051bc8f24e6b28]*/
PyObject *co_name, PyBytesObject *co_linetable)
/*[clinic end generated code: output=50d77e668d3b449b input=a5f997b173d7f636]*/
{
#define CHECK_INT_ARG(ARG) \
if (ARG < 0) { \
@ -680,7 +758,7 @@ code_replace_impl(PyCodeObject *self, int co_argcount,
co_argcount, co_posonlyargcount, co_kwonlyargcount, co_nlocals,
co_stacksize, co_flags, (PyObject*)co_code, co_consts, co_names,
co_varnames, co_freevars, co_cellvars, co_filename, co_name,
co_firstlineno, (PyObject*)co_lnotab);
co_firstlineno, (PyObject*)co_linetable);
}
static PyObject *
@ -933,10 +1011,189 @@ code_hash(PyCodeObject *co)
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->lo_next)[-1];
if (ldelta == -128) {
ldelta = 0;
}
bounds->ar_computed_line -= ldelta;
bounds->lo_next -= 2;
bounds->ar_end = bounds->ar_start;
bounds->ar_start -= ((unsigned char *)bounds->lo_next)[-2];
ldelta = ((signed char *)bounds->lo_next)[-1];
if (ldelta == -128) {
bounds->ar_line = -1;
}
else {
bounds->ar_line = bounds->ar_computed_line;
}
}
static void
advance(PyCodeAddressRange *bounds)
{
bounds->ar_start = bounds->ar_end;
int delta = ((unsigned char *)bounds->lo_next)[0];
assert (delta < 255);
bounds->ar_end += delta;
int ldelta = ((signed char *)bounds->lo_next)[1];
bounds->lo_next += 2;
if (ldelta == -128) {
bounds->ar_line = -1;
}
else {
bounds->ar_computed_line += ldelta;
bounds->ar_line = bounds->ar_computed_line;
}
}
static inline int
at_end(PyCodeAddressRange *bounds) {
return ((unsigned char *)bounds->lo_next)[0] == 255;
}
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 */
};
@ -971,7 +1228,7 @@ PyTypeObject PyCode_Type = {
0, /* tp_iternext */
code_methods, /* tp_methods */
code_memberlist, /* tp_members */
0, /* tp_getset */
code_getsetlist, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
@ -982,78 +1239,55 @@ PyTypeObject PyCode_Type = {
code_new, /* tp_new */
};
/* Use co_lnotab to compute the line number from a bytecode index, addrq. See
/* 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)
{
Py_ssize_t size = PyBytes_Size(co->co_lnotab) / 2;
unsigned char *p = (unsigned char*)PyBytes_AsString(co->co_lnotab);
int line = co->co_firstlineno;
int addr = 0;
while (--size >= 0) {
addr += *p++;
if (addr > addrq)
break;
line += (signed char)*p;
p++;
if (addrq == -1) {
return co->co_firstlineno;
}
return line;
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, int firstlineno, PyCodeAddressRange *range)
{
range->lo_next = linetable;
range->ar_start = -1;
range->ar_end = 0;
range->ar_computed_line = range->ar_line = firstlineno;
}
int
_PyCode_InitAddressRange(PyCodeObject* co, PyCodeAddressRange *bounds)
{
char *linetable = PyBytes_AS_STRING(co->co_linetable);
PyLineTable_InitAddressRange(linetable, 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. */
the same line as lasti. Return the number of that line, or -1 if lasti is out of bounds. */
int
_PyCode_CheckLineNumber(PyCodeObject* co, int lasti, PyAddrPair *bounds)
_PyCode_CheckLineNumber(int lasti, PyCodeAddressRange *bounds)
{
Py_ssize_t size;
int addr, line;
unsigned char* p;
p = (unsigned char*)PyBytes_AS_STRING(co->co_lnotab);
size = PyBytes_GET_SIZE(co->co_lnotab) / 2;
addr = 0;
line = co->co_firstlineno;
assert(line > 0);
/* possible optimization: if f->f_lasti == instr_ub
(likely to be a common case) then we already know
instr_lb -- if we stored the matching value of p
somewhere we could skip the first while loop. */
/* See lnotab_notes.txt for the description of
co_lnotab. A point to remember: increments to p
come in (addr, line) pairs. */
bounds->ap_lower = 0;
while (size > 0) {
if (addr + *p > lasti)
break;
addr += *p++;
if ((signed char)*p)
bounds->ap_lower = addr;
line += (signed char)*p;
p++;
--size;
}
if (size > 0) {
while (--size >= 0) {
addr += *p++;
if ((signed char)*p)
break;
p++;
while (bounds->ar_end <= lasti) {
if (!PyLineTable_NextAddressRange(bounds)) {
return -1;
}
bounds->ap_upper = addr;
}
else {
bounds->ap_upper = INT_MAX;
while (bounds->ar_start > lasti) {
if (!PyLineTable_PreviousAddressRange(bounds)) {
return -1;
}
}
return line;
return bounds->ar_line;
}