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

@ -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;
}
}