Skip to content

Commit 7a4b3e0

Browse files
committed
BasicBlock.__iter__() rejects labels
1 parent ebfcb25 commit 7a4b3e0

3 files changed

Lines changed: 19 additions & 20 deletions

File tree

bytecode/cfg.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,15 @@ def __iter__(self):
2323
"but %s was found"
2424
% instr.__class__.__name__)
2525

26-
if index < len(self):
27-
if isinstance(instr, Instr) and instr.has_jump():
26+
if isinstance(instr, Instr) and instr.has_jump():
27+
if index < len(self):
2828
raise ValueError("Only the last instruction of a basic "
2929
"block can be a jump")
3030

31+
if not isinstance(instr.arg, BasicBlock):
32+
raise ValueError("Jump target must a BasicBlock, got %s",
33+
type(instr.arg).__name__)
34+
3135
yield instr
3236

3337

@@ -217,7 +221,6 @@ def to_bytecode(self):
217221
if isinstance(instr, Instr) and isinstance(instr.arg, BasicBlock):
218222
used_blocks.add(id(instr.arg))
219223

220-
221224
labels = {}
222225
jumps = []
223226
instructions = []

bytecode/tests/test_cfg.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,27 @@
77

88
class BlockTests(unittest.TestCase):
99
def test_iter_invalid_types(self):
10+
# Labels are not allowed in basic blocks
1011
block = BasicBlock()
1112
block.append(Label())
1213
with self.assertRaises(ValueError):
1314
list(block)
1415

16+
# Only one jump allowed and only at the end
1517
block = BasicBlock()
1618
block2 = BasicBlock()
1719
block.extend([Instr('JUMP_ABSOLUTE', block2),
1820
Instr('NOP')])
1921
with self.assertRaises(ValueError):
2022
list(block)
2123

24+
# jump target must be a BasicBlock
25+
block = BasicBlock()
26+
label = Label()
27+
block.extend([Instr('JUMP_ABSOLUTE', label)])
28+
with self.assertRaises(ValueError):
29+
list(block)
30+
2231

2332
class BytecodeBlocksTests(TestCase):
2433
maxDiff = 80 * 100
@@ -136,20 +145,6 @@ def test_to_bytecode(self):
136145
Instr('RETURN_VALUE', lineno=3)])
137146
# FIXME: test other attributes
138147

139-
def test_to_bytecode_labels(self):
140-
blocks = ControlFlowGraph()
141-
label = Label()
142-
blocks.add_block()
143-
blocks[0].extend([Instr('LOAD_NAME', 'test'),
144-
Instr('POP_JUMP_IF_FALSE', label)])
145-
blocks[1].append(label)
146-
147-
with self.assertRaises(ValueError) as cm:
148-
blocks.to_bytecode()
149-
self.assertEqual(str(cm.exception),
150-
'BasicBlock must only contain SetLineno, Instr and '
151-
'ConcreteInstr objects, but Label was found')
152-
153148
def test_from_bytecode(self):
154149
bytecode = Bytecode()
155150
label = Label()

doc/api.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -449,13 +449,16 @@ BasicBlock
449449
It is possible to use concrete instructions (:class:`ConcreteInstr`) in
450450
blocks, but abstract instructions (:class:`Instr`) are preferred.
451451

452+
Only the last instruction can have a jump, and the jump target must be a
453+
basic block (:class:`BasicBlock`).
454+
452455
Labels (:class:`Label`) must not be used in blocks.
453456

454457
Attributes:
455458

456459
.. attribute:: next_block
457460

458-
Next block (:class:`BasicBlock`), or ``None``.
461+
Next basic block (:class:`BasicBlock`), or ``None``.
459462

460463

461464
ControlFlowGraph
@@ -470,8 +473,6 @@ ControlFlowGraph
470473
entry and no branches out except at the exit. Inherit from
471474
:class:`BaseBytecode`.
472475

473-
Jump targets are blocks (:class:`BasicBlock`).
474-
475476
Labels (:class:`Label`) must not be used in blocks.
476477

477478
This class is not designed to emit code, but to analyze and modify existing

0 commit comments

Comments
 (0)