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

@ -66,8 +66,8 @@ static void call_exc_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *);
static int maybe_call_line_trace(Py_tracefunc, PyObject *,
PyThreadState *, PyFrameObject *,
int *, int *, int *);
static void maybe_dtrace_line(PyFrameObject *, int *, int *, int *);
PyCodeAddressRange *, int *);
static void maybe_dtrace_line(PyFrameObject *, PyCodeAddressRange *, int *);
static void dtrace_function_entry(PyFrameObject *);
static void dtrace_function_return(PyFrameObject *);
@ -976,7 +976,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
is true when the line being executed has changed. The
initial values are such as to make this false the first
time it is tested. */
int instr_ub = -1, instr_lb = 0, instr_prev = -1;
const _Py_CODEUNIT *first_instr;
PyObject *names;
@ -1390,6 +1389,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
dtrace_function_entry(f);
co = f->f_code;
PyCodeAddressRange bounds;
_PyCode_InitAddressRange(co, &bounds);
int instr_prev = -1;
names = co->co_names;
consts = co->co_consts;
fastlocals = f->f_localsplus;
@ -1514,7 +1517,7 @@ main_loop:
f->f_lasti = INSTR_OFFSET();
if (PyDTrace_LINE_ENABLED())
maybe_dtrace_line(f, &instr_lb, &instr_ub, &instr_prev);
maybe_dtrace_line(f, &bounds, &instr_prev);
/* line-by-line tracing support */
@ -1528,7 +1531,7 @@ main_loop:
err = maybe_call_line_trace(tstate->c_tracefunc,
tstate->c_traceobj,
tstate, f,
&instr_lb, &instr_ub, &instr_prev);
&bounds, &instr_prev);
/* Reload possibly changed frame fields */
JUMPTO(f->f_lasti);
stack_pointer = f->f_valuestack+f->f_stackdepth;
@ -4039,14 +4042,7 @@ exception_unwind:
PUSH(exc);
JUMPTO(handler);
if (_Py_TracingPossible(ceval2)) {
int needs_new_execution_window = (f->f_lasti < instr_lb || f->f_lasti >= instr_ub);
int needs_line_update = (f->f_lasti == instr_lb || f->f_lasti < instr_prev);
/* Make sure that we trace line after exception if we are in a new execution
* window or we don't need a line update and we are not in the first instruction
* of the line. */
if (needs_new_execution_window || (!needs_line_update && instr_lb > 0)) {
instr_prev = INT_MAX;
}
instr_prev = INT_MAX;
}
/* Resume normal execution */
f->f_state = FRAME_EXECUTING;
@ -4960,7 +4956,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args)
static int
maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyThreadState *tstate, PyFrameObject *frame,
int *instr_lb, int *instr_ub, int *instr_prev)
PyCodeAddressRange *bounds, int *instr_prev)
{
int result = 0;
int line = frame->f_lineno;
@ -4968,21 +4964,17 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
/* If the last instruction executed isn't in the current
instruction window, reset the window.
*/
if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
PyAddrPair bounds;
line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
&bounds);
*instr_lb = bounds.ap_lower;
*instr_ub = bounds.ap_upper;
}
line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
/* If the last instruction falls at the start of a line or if it
represents a jump backwards, update the frame's line number and
then call the trace function if we're tracing source lines.
*/
if ((frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev)) {
frame->f_lineno = line;
if (frame->f_trace_lines) {
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
if ((line != frame->f_lineno || frame->f_lasti < *instr_prev)) {
if (line != -1) {
frame->f_lineno = line;
if (frame->f_trace_lines) {
result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None);
}
}
}
/* Always emit an opcode event if we're tracing all opcodes. */
@ -5907,33 +5899,28 @@ dtrace_function_return(PyFrameObject *f)
/* DTrace equivalent of maybe_call_line_trace. */
static void
maybe_dtrace_line(PyFrameObject *frame,
int *instr_lb, int *instr_ub, int *instr_prev)
PyCodeAddressRange *bounds, int *instr_prev)
{
int line = frame->f_lineno;
const char *co_filename, *co_name;
/* If the last instruction executed isn't in the current
instruction window, reset the window.
*/
if (frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub) {
PyAddrPair bounds;
line = _PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
&bounds);
*instr_lb = bounds.ap_lower;
*instr_ub = bounds.ap_upper;
}
int line = _PyCode_CheckLineNumber(frame->f_lasti, bounds);
/* If the last instruction falls at the start of a line or if
it represents a jump backwards, update the frame's line
number and call the trace function. */
if (frame->f_lasti == *instr_lb || frame->f_lasti < *instr_prev) {
frame->f_lineno = line;
co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
if (!co_filename)
co_filename = "?";
co_name = PyUnicode_AsUTF8(frame->f_code->co_name);
if (!co_name)
co_name = "?";
PyDTrace_LINE(co_filename, co_name, line);
if (line != frame->f_lineno || frame->f_lasti < *instr_prev) {
if (line != -1) {
frame->f_lineno = line;
co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename);
if (!co_filename)
co_filename = "?";
co_name = PyUnicode_AsUTF8(frame->f_code->co_name);
if (!co_name)
co_name = "?";
PyDTrace_LINE(co_filename, co_name, line);
}
}
*instr_prev = frame->f_lasti;
}

View file

@ -1827,7 +1827,7 @@ compiler_mod(struct compiler *c, mod_ty mod)
return NULL;
}
/* Use 0 for firstlineno initially, will fixup in assemble(). */
if (!compiler_enter_scope(c, module, COMPILER_SCOPE_MODULE, mod, 0))
if (!compiler_enter_scope(c, module, COMPILER_SCOPE_MODULE, mod, 1))
return NULL;
switch (mod->kind) {
case Module_kind:
@ -2271,7 +2271,9 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
c->u->u_argcount = asdl_seq_LEN(args->args);
c->u->u_posonlyargcount = asdl_seq_LEN(args->posonlyargs);
c->u->u_kwonlyargcount = asdl_seq_LEN(args->kwonlyargs);
VISIT_SEQ_IN_SCOPE(c, stmt, body);
for (i = docstring ? 1 : 0; i < asdl_seq_LEN(body); i++) {
VISIT(c, stmt, (stmt_ty)asdl_seq_GET(body, i));
}
co = assemble(c, 1);
qualname = c->u->u_qualname;
Py_INCREF(qualname);
@ -2808,6 +2810,8 @@ compiler_async_for(struct compiler *c, stmt_ty s)
/* Except block for __anext__ */
compiler_use_next_block(c, except);
c->u->u_lineno = -1;
ADDOP(c, END_ASYNC_FOR);
/* `else` block */
@ -3115,7 +3119,8 @@ compiler_try_except(struct compiler *c, stmt_ty s)
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
ADDOP(c, POP_BLOCK);
ADDOP(c, POP_EXCEPT);
/* name = None; del name */
/* name = None; del name; # Mark as artificial */
c->u->u_lineno = -1;
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
@ -3124,7 +3129,8 @@ compiler_try_except(struct compiler *c, stmt_ty s)
/* except: */
compiler_use_next_block(c, cleanup_end);
/* name = None; del name */
/* name = None; del name; # Mark as artificial */
c->u->u_lineno = -1;
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
@ -3359,6 +3365,7 @@ compiler_visit_stmt_expr(struct compiler *c, expr_ty value)
if (value->kind == Constant_kind) {
/* ignore constant statement */
ADDOP(c, NOP);
return 1;
}
@ -3431,6 +3438,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
case Expr_kind:
return compiler_visit_stmt_expr(c, s->v.Expr.value);
case Pass_kind:
ADDOP(c, NOP);
break;
case Break_kind:
return compiler_break(c);
@ -5429,8 +5437,9 @@ struct assembler {
basicblock **a_reverse_postorder; /* list of blocks in dfs postorder */
PyObject *a_lnotab; /* string containing lnotab */
int a_lnotab_off; /* offset into lnotab */
int a_lineno; /* last lineno of emitted instruction */
int a_lineno_off; /* bytecode offset of last lineno */
int a_prevlineno; /* lineno of last emitted line in line table */
int a_lineno; /* lineno of last emitted instruction */
int a_lineno_start; /* bytecode start offset of current lineno */
};
static void
@ -5533,7 +5542,7 @@ static int
assemble_init(struct assembler *a, int nblocks, int firstlineno)
{
memset(a, 0, sizeof(struct assembler));
a->a_lineno = firstlineno;
a->a_prevlineno = a->a_lineno = firstlineno;
a->a_bytecode = PyBytes_FromStringAndSize(NULL, DEFAULT_CODE_SIZE);
if (!a->a_bytecode)
return 0;
@ -5573,114 +5582,82 @@ blocksize(basicblock *b)
return size;
}
/* Appends a pair to the end of the line number table, a_lnotab, representing
the instruction's bytecode offset and line number. See
Objects/lnotab_notes.txt for the description of the line number table. */
static int
assemble_lnotab(struct assembler *a, struct instr *i)
assemble_emit_linetable_pair(struct assembler *a, int bdelta, int ldelta)
{
int d_bytecode, d_lineno;
Py_ssize_t len;
unsigned char *lnotab;
d_lineno = i->i_lineno - a->a_lineno;
if (d_lineno == 0) {
return 1;
}
d_bytecode = (a->a_offset - a->a_lineno_off) * sizeof(_Py_CODEUNIT);
assert(d_bytecode >= 0);
if (d_bytecode > 255) {
int j, nbytes, ncodes = d_bytecode / 255;
nbytes = a->a_lnotab_off + 2 * ncodes;
len = PyBytes_GET_SIZE(a->a_lnotab);
if (nbytes >= len) {
if ((len <= INT_MAX / 2) && (len * 2 < nbytes))
len = nbytes;
else if (len <= INT_MAX / 2)
len *= 2;
else {
PyErr_NoMemory();
return 0;
}
if (_PyBytes_Resize(&a->a_lnotab, len) < 0)
return 0;
}
lnotab = (unsigned char *)
PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
for (j = 0; j < ncodes; j++) {
*lnotab++ = 255;
*lnotab++ = 0;
}
d_bytecode -= ncodes * 255;
a->a_lnotab_off += ncodes * 2;
}
assert(0 <= d_bytecode && d_bytecode <= 255);
if (d_lineno < -128 || 127 < d_lineno) {
int j, nbytes, ncodes, k;
if (d_lineno < 0) {
k = -128;
/* use division on positive numbers */
ncodes = (-d_lineno) / 128;
}
else {
k = 127;
ncodes = d_lineno / 127;
}
d_lineno -= ncodes * k;
assert(ncodes >= 1);
nbytes = a->a_lnotab_off + 2 * ncodes;
len = PyBytes_GET_SIZE(a->a_lnotab);
if (nbytes >= len) {
if ((len <= INT_MAX / 2) && len * 2 < nbytes)
len = nbytes;
else if (len <= INT_MAX / 2)
len *= 2;
else {
PyErr_NoMemory();
return 0;
}
if (_PyBytes_Resize(&a->a_lnotab, len) < 0)
return 0;
}
lnotab = (unsigned char *)
PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
*lnotab++ = d_bytecode;
*lnotab++ = k;
d_bytecode = 0;
for (j = 1; j < ncodes; j++) {
*lnotab++ = 0;
*lnotab++ = k;
}
a->a_lnotab_off += ncodes * 2;
}
assert(-128 <= d_lineno && d_lineno <= 127);
len = PyBytes_GET_SIZE(a->a_lnotab);
Py_ssize_t len = PyBytes_GET_SIZE(a->a_lnotab);
if (a->a_lnotab_off + 2 >= len) {
if (_PyBytes_Resize(&a->a_lnotab, len * 2) < 0)
return 0;
}
lnotab = (unsigned char *)
unsigned char *lnotab = (unsigned char *)
PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
a->a_lnotab_off += 2;
if (d_bytecode) {
*lnotab++ = d_bytecode;
*lnotab++ = d_lineno;
}
else { /* First line of a block; def stmt, etc. */
*lnotab++ = 0;
*lnotab++ = d_lineno;
}
a->a_lineno = i->i_lineno;
a->a_lineno_off = a->a_offset;
*lnotab++ = bdelta;
*lnotab++ = ldelta;
return 1;
}
/* Appends a range to the end of the line number table. See
* Objects/lnotab_notes.txt for the description of the line number table. */
static int
assemble_line_range(struct assembler *a)
{
int ldelta, bdelta;
bdelta = (a->a_offset - a->a_lineno_start) * 2;
if (bdelta == 0) {
return 1;
}
if (a->a_lineno < 0) {
ldelta = -128;
}
else {
ldelta = a->a_lineno - a->a_prevlineno;
a->a_prevlineno = a->a_lineno;
while (ldelta > 127) {
if (!assemble_emit_linetable_pair(a, 0, 127)) {
return 0;
}
ldelta -= 127;
}
while (ldelta < -127) {
if (!assemble_emit_linetable_pair(a, 0, -127)) {
return 0;
}
ldelta += 127;
}
}
assert(-128 <= ldelta && ldelta < 128);
while (bdelta > 254) {
if (!assemble_emit_linetable_pair(a, 254, ldelta)) {
return 0;
}
ldelta = a->a_lineno < 0 ? -128 : 0;
bdelta -= 254;
}
if (!assemble_emit_linetable_pair(a, bdelta, ldelta)) {
return 0;
}
a->a_lineno_start = a->a_offset;
return 1;
}
static int
assemble_lnotab(struct assembler *a, struct instr *i)
{
if (i->i_lineno == a->a_lineno) {
return 1;
}
if (!assemble_line_range(a)) {
return 0;
}
a->a_lineno = i->i_lineno;
return 1;
}
/* assemble_emit()
Extend the bytecode with a new instruction.
Update lnotab if necessary.
@ -5998,7 +5975,7 @@ assemble(struct compiler *c, int addNone)
block ends with a jump or return b_next shouldn't set.
*/
if (!c->u->u_curblock->b_return) {
NEXT_BLOCK(c);
c->u->u_lineno = -1;
if (addNone)
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, RETURN_VALUE);
@ -6015,7 +5992,7 @@ assemble(struct compiler *c, int addNone)
if (!c->u->u_firstlineno) {
if (entryblock && entryblock->b_instr && entryblock->b_instr->i_lineno)
c->u->u_firstlineno = entryblock->b_instr->i_lineno;
else
else
c->u->u_firstlineno = 1;
}
if (!assemble_init(&a, nblocks, c->u->u_firstlineno))
@ -6040,6 +6017,13 @@ assemble(struct compiler *c, int addNone)
if (!assemble_emit(&a, &b->b_instr[j]))
goto error;
}
if (!assemble_line_range(&a)) {
return 0;
}
/* Emit sentinel at end of line number table */
if (!assemble_emit_linetable_pair(&a, 255, -128)) {
goto error;
}
if (_PyBytes_Resize(&a.a_lnotab, a.a_lnotab_off) < 0)
goto error;
@ -6280,22 +6264,42 @@ static void
clean_basic_block(basicblock *bb) {
/* Remove NOPs and any code following a return or re-raise. */
int dest = 0;
int prev_lineno = -1;
for (int src = 0; src < bb->b_iused; src++) {
int lineno = bb->b_instr[src].i_lineno;
switch(bb->b_instr[src].i_opcode) {
case NOP:
/* skip */
break;
case RETURN_VALUE:
case RERAISE:
bb->b_next = NULL;
bb->b_instr[dest] = bb->b_instr[src];
dest++;
goto end;
case NOP:
{
/* Eliminate no-op if it doesn't have a line number, or
* if the next instruction has same line number or no line number, or
* if the previous instruction had the same line number. */
if (lineno < 0) {
break;
}
if (prev_lineno == lineno) {
break;
}
if (src < bb->b_iused - 1) {
int next_lineno = bb->b_instr[src+1].i_lineno;
if (next_lineno < 0 || next_lineno == lineno) {
bb->b_instr[src+1].i_lineno = lineno;
break;
}
}
}
/* fallthrough */
default:
if (dest != src) {
bb->b_instr[dest] = bb->b_instr[src];
}
dest++;
prev_lineno = lineno;
break;
}
}

3051
Python/importlib.h generated

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -524,7 +524,7 @@ w_complex_object(PyObject *v, char flag, WFILE *p)
w_object(co->co_filename, p);
w_object(co->co_name, p);
w_long(co->co_firstlineno, p);
w_object(co->co_lnotab, p);
w_object(co->co_linetable, p);
}
else if (PyObject_CheckBuffer(v)) {
/* Write unknown bytes-like objects as a bytes object */
@ -1312,7 +1312,7 @@ r_object(RFILE *p)
PyObject *filename = NULL;
PyObject *name = NULL;
int firstlineno;
PyObject *lnotab = NULL;
PyObject *linetable = NULL;
idx = r_ref_reserve(flag, p);
if (idx < 0)
@ -1367,8 +1367,8 @@ r_object(RFILE *p)
firstlineno = (int)r_long(p);
if (firstlineno == -1 && PyErr_Occurred())
break;
lnotab = r_object(p);
if (lnotab == NULL)
linetable = r_object(p);
if (linetable == NULL)
goto code_error;
if (PySys_Audit("code.__new__", "OOOiiiiii",
@ -1382,7 +1382,7 @@ r_object(RFILE *p)
nlocals, stacksize, flags,
code, consts, names, varnames,
freevars, cellvars, filename, name,
firstlineno, lnotab);
firstlineno, linetable);
v = r_ref_insert(v, idx, flag, p);
code_error:
@ -1394,7 +1394,7 @@ r_object(RFILE *p)
Py_XDECREF(cellvars);
Py_XDECREF(filename);
Py_XDECREF(name);
Py_XDECREF(lnotab);
Py_XDECREF(linetable);
}
retval = v;
break;