summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2020-11-12 09:43:29 (GMT)
committerGitHub <noreply@github.com>2020-11-12 09:43:29 (GMT)
commit877df851c3ecdb55306840e247596e7b7805a60a (patch)
treeec00c0af84f9f228d78e23e8c8b38201129f8fae /Python/compile.c
parentcda99b4022daa08ac74b0420e9903cce883d91c6 (diff)
downloadcpython-877df851c3ecdb55306840e247596e7b7805a60a.zip
cpython-877df851c3ecdb55306840e247596e7b7805a60a.tar.gz
cpython-877df851c3ecdb55306840e247596e7b7805a60a.tar.bz2
bpo-42246: Partial implementation of PEP 626. (GH-23113)
* Implement new line number table format, as defined in PEP 626.
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c202
1 files changed, 103 insertions, 99 deletions
diff --git a/Python/compile.c b/Python/compile.c
index 15a9046..b4f2ceb 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -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;
+ 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;
}
+ unsigned char *lnotab = (unsigned char *)
+ PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
+
+ a->a_lnotab_off += 2;
+ *lnotab++ = bdelta;
+ *lnotab++ = ldelta;
+ return 1;
+}
- d_bytecode = (a->a_offset - a->a_lineno_off) * sizeof(_Py_CODEUNIT);
- assert(d_bytecode >= 0);
+/* Appends a range to the end of the line number table. See
+ * Objects/lnotab_notes.txt for the description of the line number table. */
- 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();
+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;
}
- 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;
+ ldelta -= 127;
}
- 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();
+ while (ldelta < -127) {
+ if (!assemble_emit_linetable_pair(a, 0, -127)) {
return 0;
}
- if (_PyBytes_Resize(&a->a_lnotab, len) < 0)
- return 0;
+ ldelta += 127;
}
- 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);
- if (a->a_lnotab_off + 2 >= len) {
- if (_PyBytes_Resize(&a->a_lnotab, len * 2) < 0)
+ 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;
}
- lnotab = (unsigned char *)
- PyBytes_AS_STRING(a->a_lnotab) + a->a_lnotab_off;
+ if (!assemble_emit_linetable_pair(a, bdelta, ldelta)) {
+ return 0;
+ }
+ a->a_lineno_start = a->a_offset;
+ return 1;
+}
- a->a_lnotab_off += 2;
- if (d_bytecode) {
- *lnotab++ = d_bytecode;
- *lnotab++ = d_lineno;
+static int
+assemble_lnotab(struct assembler *a, struct instr *i)
+{
+ if (i->i_lineno == a->a_lineno) {
+ return 1;
}
- else { /* First line of a block; def stmt, etc. */
- *lnotab++ = 0;
- *lnotab++ = d_lineno;
+ if (!assemble_line_range(a)) {
+ return 0;
}
a->a_lineno = i->i_lineno;
- a->a_lineno_off = a->a_offset;
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;
}
}