Skip to content

Commit 58d4b8b

Browse files
committed
compiler: support artificial instructions
1 parent b47cdae commit 58d4b8b

File tree

6 files changed

+6376
-6448
lines changed

6 files changed

+6376
-6448
lines changed

Lib/test/test_code.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@
129129
import sys
130130
import threading
131131
import unittest
132+
import textwrap
132133
import weakref
134+
133135
try:
134136
import ctypes
135137
except ImportError:
@@ -315,6 +317,53 @@ def func():
315317
new_code = code = func.__code__.replace(co_linetable=b'')
316318
self.assertEqual(list(new_code.co_lines()), [])
317319

320+
def test_co_positions_artificial_instructions(self):
321+
import dis
322+
323+
namespace = {}
324+
exec(textwrap.dedent("""\
325+
try:
326+
1/0
327+
except Exception as e:
328+
exc = e
329+
"""), namespace)
330+
331+
exc = namespace['exc']
332+
traceback = exc.__traceback__
333+
code = traceback.tb_frame.f_code
334+
335+
artificial_instructions = []
336+
for instr, positions in zip(
337+
dis.get_instructions(code),
338+
code.co_positions(),
339+
strict=True
340+
):
341+
# If any of the positions is None, then all have to
342+
# be None as well for the case above. There are still
343+
# some places in the compiler, where the artificial instructions
344+
# get assigned the first_lineno but they don't have other positions.
345+
# There is no easy way of inferring them at that stage, so for now
346+
# we don't support it.
347+
self.assertTrue(all(positions) or not any(positions))
348+
349+
if not any(positions):
350+
artificial_instructions.append(instr)
351+
352+
self.assertEqual(
353+
[
354+
(instruction.opname, instruction.argval)
355+
for instruction in artificial_instructions
356+
],
357+
[
358+
("PUSH_EXC_INFO", None),
359+
("LOAD_CONST", None), # artificial 'None'
360+
("STORE_NAME", "e"), # XX: we know the location for this
361+
("DELETE_NAME", "e"),
362+
("RERAISE", 1),
363+
("POP_EXCEPT_AND_RERAISE", None)
364+
]
365+
)
366+
318367
# co_positions behavior when info is missing.
319368

320369
def test_co_positions_empty_linetable(self):

Programs/test_frozenmain.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/compile.c

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,19 @@ compiler_next_instr(basicblock *b)
967967
(c)->u->u_end_lineno = (x)->end_lineno; \
968968
(c)->u->u_end_col_offset = (x)->end_col_offset;
969969

970+
// Artificial instructions
971+
#define UNSET_LOC(c) \
972+
(c)->u->u_lineno = -1; \
973+
(c)->u->u_col_offset = -1; \
974+
(c)->u->u_end_lineno = -1; \
975+
(c)->u->u_end_col_offset = -1;
976+
977+
#define COPY_INSTR_LOC(old, new) \
978+
(new).i_lineno = (old).i_lineno; \
979+
(new).i_col_offset = (old).i_col_offset; \
980+
(new).i_end_lineno = (old).i_end_lineno; \
981+
(new).i_end_col_offset = (old).i_end_col_offset;
982+
970983
/* Return the stack effect of opcode with argument oparg.
971984
972985
Some opcodes have different stack effect when jump to the target and
@@ -1858,8 +1871,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
18581871
/* The finally block should appear to execute after the
18591872
* statement causing the unwinding, so make the unwinding
18601873
* instruction artificial */
1861-
c->u->u_lineno = -1;
1862-
c->u->u_end_lineno = -1;
1874+
UNSET_LOC(c);
18631875
return 1;
18641876

18651877
case FINALLY_END:
@@ -1895,7 +1907,7 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
18951907
/* The exit block should appear to execute after the
18961908
* statement causing the unwinding, so make the unwinding
18971909
* instruction artificial */
1898-
c->u->u_lineno = -1;
1910+
UNSET_LOC(c);
18991911
return 1;
19001912

19011913
case HANDLER_CLEANUP:
@@ -2547,7 +2559,7 @@ compiler_class(struct compiler *c, stmt_ty s)
25472559
return 0;
25482560
}
25492561
/* The following code is artificial */
2550-
c->u->u_lineno = -1;
2562+
UNSET_LOC(c);
25512563
/* Return __classcell__ if it is referenced, otherwise return None */
25522564
if (c->u->u_ste->ste_needs_class_closure) {
25532565
/* Store __classcell__ into class namespace & return it */
@@ -2938,7 +2950,7 @@ compiler_for(struct compiler *c, stmt_ty s)
29382950
VISIT(c, expr, s->v.For.target);
29392951
VISIT_SEQ(c, stmt, s->v.For.body);
29402952
/* Mark jump as artificial */
2941-
c->u->u_lineno = -1;
2953+
UNSET_LOC(c);
29422954
ADDOP_JUMP(c, JUMP_ABSOLUTE, start);
29432955
compiler_use_next_block(c, cleanup);
29442956

@@ -2991,7 +3003,7 @@ compiler_async_for(struct compiler *c, stmt_ty s)
29913003
/* Except block for __anext__ */
29923004
compiler_use_next_block(c, except);
29933005

2994-
c->u->u_lineno = -1;
3006+
UNSET_LOC(c);
29953007
ADDOP(c, END_ASYNC_FOR);
29963008

29973009
/* `else` block */
@@ -3178,7 +3190,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
31783190
/* `finally` block */
31793191
compiler_use_next_block(c, end);
31803192

3181-
c->u->u_lineno = -1;
3193+
UNSET_LOC(c);
31823194
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
31833195
ADDOP(c, PUSH_EXC_INFO);
31843196
if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL))
@@ -3246,7 +3258,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
32463258
n = asdl_seq_LEN(s->v.Try.handlers);
32473259
compiler_use_next_block(c, except);
32483260

3249-
c->u->u_lineno = -1;
3261+
UNSET_LOC(c);
32503262
ADDOP_JUMP(c, SETUP_CLEANUP, cleanup);
32513263
ADDOP(c, PUSH_EXC_INFO);
32523264
/* Runtime will push a block here, so we need to account for that */
@@ -3305,7 +3317,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
33053317
ADDOP(c, POP_BLOCK);
33063318
ADDOP(c, POP_EXCEPT);
33073319
/* name = None; del name; # Mark as artificial */
3308-
c->u->u_lineno = -1;
3320+
UNSET_LOC(c);
33093321
ADDOP_LOAD_CONST(c, Py_None);
33103322
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
33113323
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
@@ -3315,7 +3327,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
33153327
compiler_use_next_block(c, cleanup_end);
33163328

33173329
/* name = None; del name; # Mark as artificial */
3318-
c->u->u_lineno = -1;
3330+
UNSET_LOC(c);
33193331

33203332
ADDOP_LOAD_CONST(c, Py_None);
33213333
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
@@ -3338,15 +3350,15 @@ compiler_try_except(struct compiler *c, stmt_ty s)
33383350
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
33393351
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
33403352
/* name = None; del name; # Mark as artificial */
3341-
c->u->u_lineno = -1;
3353+
UNSET_LOC(c);
33423354
ADDOP(c, POP_BLOCK);
33433355
ADDOP(c, POP_EXCEPT);
33443356
ADDOP_JUMP(c, JUMP_FORWARD, end);
33453357
}
33463358
compiler_use_next_block(c, except);
33473359
}
33483360
/* Mark as artificial */
3349-
c->u->u_lineno = -1;
3361+
UNSET_LOC(c);
33503362
compiler_pop_fblock(c, EXCEPTION_HANDLER, NULL);
33513363
ADDOP_I(c, RERAISE, 0);
33523364
compiler_use_next_block(c, cleanup);
@@ -3567,7 +3579,7 @@ compiler_visit_stmt_expr(struct compiler *c, expr_ty value)
35673579

35683580
VISIT(c, expr, value);
35693581
/* Mark POP_TOP as artificial */
3570-
c->u->u_lineno = -1;
3582+
UNSET_LOC(c);
35713583
ADDOP(c, POP_TOP);
35723584
return 1;
35733585
}
@@ -4921,7 +4933,7 @@ compiler_async_comprehension_generator(struct compiler *c,
49214933
compiler_pop_fblock(c, ASYNC_COMPREHENSION_GENERATOR, start);
49224934

49234935
compiler_use_next_block(c, except);
4924-
//c->u->u_lineno = -1;
4936+
//UNSET_LOC(c);
49254937

49264938
ADDOP(c, END_ASYNC_FOR);
49274939

@@ -5101,7 +5113,7 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k)
51015113

51025114
static int
51035115
compiler_with_except_finish(struct compiler *c, basicblock * cleanup) {
5104-
c->u->u_lineno = -1;
5116+
UNSET_LOC(c);
51055117
basicblock *exit;
51065118
exit = compiler_new_block(c);
51075119
if (exit == NULL)
@@ -5296,7 +5308,7 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
52965308

52975309

52985310
/* Mark all following code as artificial */
5299-
c->u->u_lineno = -1;
5311+
UNSET_LOC(c);
53005312
ADDOP(c, POP_BLOCK);
53015313
compiler_pop_fblock(c, WITH, block);
53025314

@@ -7587,6 +7599,9 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock,
75877599
// This will get fixed in offset_derefs().
75887600
.i_oparg = oldindex,
75897601
.i_lineno = -1,
7602+
.i_col_offset = -1,
7603+
.i_end_lineno = -1,
7604+
.i_end_col_offset = -1,
75907605
.i_target = NULL,
75917606
};
75927607
if (insert_instruction(entryblock, ncellsused, &make_cell) < 0) {
@@ -7614,6 +7629,9 @@ insert_prefix_instructions(struct compiler *c, basicblock *entryblock,
76147629
.i_opcode = GEN_START,
76157630
.i_oparg = kind,
76167631
.i_lineno = -1,
7632+
.i_col_offset = -1,
7633+
.i_end_lineno = -1,
7634+
.i_end_col_offset = -1,
76177635
.i_target = NULL,
76187636
};
76197637
if (insert_instruction(entryblock, 0, &gen_start) < 0) {
@@ -7711,7 +7729,7 @@ assemble(struct compiler *c, int addNone)
77117729
block ends with a jump or return b_next shouldn't set.
77127730
*/
77137731
if (!c->u->u_curblock->b_return) {
7714-
c->u->u_lineno = -1;
7732+
UNSET_LOC(c);
77157733
if (addNone)
77167734
ADDOP_LOAD_CONST(c, Py_None);
77177735
ADDOP(c, RETURN_VALUE);
@@ -8246,7 +8264,7 @@ clean_basic_block(basicblock *bb) {
82468264
if (src < bb->b_iused - 1) {
82478265
int next_lineno = bb->b_instr[src+1].i_lineno;
82488266
if (next_lineno < 0 || next_lineno == lineno) {
8249-
bb->b_instr[src+1].i_lineno = lineno;
8267+
COPY_INSTR_LOC(bb->b_instr[src], bb->b_instr[src+1]);
82508268
continue;
82518269
}
82528270
}
@@ -8381,19 +8399,27 @@ propogate_line_numbers(struct assembler *a) {
83818399
if (b->b_iused == 0) {
83828400
continue;
83838401
}
8384-
int prev_lineno = -1;
8402+
8403+
// Not a real instruction, only to store positions
8404+
// from previous instructions and propagate them.
8405+
struct instr prev_instr = {
8406+
.i_lineno = -1,
8407+
.i_col_offset = -1,
8408+
.i_end_lineno = -1,
8409+
.i_end_col_offset = -1,
8410+
};
83858411
for (int i = 0; i < b->b_iused; i++) {
83868412
if (b->b_instr[i].i_lineno < 0) {
8387-
b->b_instr[i].i_lineno = prev_lineno;
8413+
COPY_INSTR_LOC(prev_instr, b->b_instr[i]);
83888414
}
83898415
else {
8390-
prev_lineno = b->b_instr[i].i_lineno;
8416+
COPY_INSTR_LOC(b->b_instr[i], prev_instr);
83918417
}
83928418
}
83938419
if (!b->b_nofallthrough && b->b_next->b_predecessors == 1) {
83948420
assert(b->b_next->b_iused);
83958421
if (b->b_next->b_instr[0].i_lineno < 0) {
8396-
b->b_next->b_instr[0].i_lineno = prev_lineno;
8422+
COPY_INSTR_LOC(prev_instr, b->b_next->b_instr[0]);
83978423
}
83988424
}
83998425
if (is_jump(&b->b_instr[b->b_iused-1])) {
@@ -8407,7 +8433,7 @@ propogate_line_numbers(struct assembler *a) {
84078433
basicblock *target = b->b_instr[b->b_iused-1].i_target;
84088434
if (target->b_predecessors == 1) {
84098435
if (target->b_instr[0].i_lineno < 0) {
8410-
target->b_instr[0].i_lineno = prev_lineno;
8436+
COPY_INSTR_LOC(prev_instr, target->b_instr[0]);
84118437
}
84128438
}
84138439
}
@@ -8510,7 +8536,7 @@ ensure_exits_have_lineno(struct compiler *c)
85108536
if (new_target == NULL) {
85118537
return -1;
85128538
}
8513-
new_target->b_instr[0].i_lineno = b->b_instr[b->b_iused-1].i_lineno;
8539+
COPY_INSTR_LOC(b->b_instr[b->b_iused-1], new_target->b_instr[0]);
85148540
b->b_instr[b->b_iused-1].i_target = new_target;
85158541
}
85168542
}
@@ -8531,7 +8557,7 @@ ensure_exits_have_lineno(struct compiler *c)
85318557
if (!b->b_nofallthrough && b->b_next && b->b_iused > 0) {
85328558
if (is_exit_without_lineno(b->b_next)) {
85338559
assert(b->b_next->b_iused > 0);
8534-
b->b_next->b_instr[0].i_lineno = b->b_instr[b->b_iused-1].i_lineno;
8560+
COPY_INSTR_LOC(b->b_instr[b->b_iused-1], b->b_next->b_instr[0]);
85358561
}
85368562
}
85378563
}

0 commit comments

Comments
 (0)