Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e4685ec

Browse files
committed
Track the block stack more reasonably in order to handle continue in
try/except or try/finally. Previous versions had only track SETUP_LOOP blocks and ignored the exception part. This meant that it allowed continue inside a try/except but generated buggy code. Now it does the right thing.
1 parent 9263848 commit e4685ec

2 files changed

Lines changed: 90 additions & 26 deletions

File tree

Lib/compiler/pycodegen.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
(1,1) : "CALL_FUNCTION_VAR_KW",
2929
}
3030

31+
LOOP = 1
32+
EXCEPT = 2
33+
TRY_FINALLY = 3
34+
END_FINALLY = 4
35+
3136
def compile(filename, display=0):
3237
f = open(filename)
3338
buf = f.read()
@@ -142,7 +147,7 @@ def __init__(self, filename):
142147
self.checkClass()
143148
self.filename = filename
144149
self.locals = misc.Stack()
145-
self.loops = misc.Stack()
150+
self.setups = misc.Stack()
146151
self.curStack = 0
147152
self.maxStack = 0
148153
self.last_lineno = None
@@ -327,7 +332,7 @@ def visitWhile(self, node):
327332
self.emit('SETUP_LOOP', after)
328333

329334
self.nextBlock(loop)
330-
self.loops.push(loop)
335+
self.setups.push((LOOP, loop))
331336

332337
self.set_lineno(node, force=1)
333338
self.visit(node.test)
@@ -341,7 +346,7 @@ def visitWhile(self, node):
341346
self.startBlock(else_) # or just the POPs if not else clause
342347
self.emit('POP_TOP')
343348
self.emit('POP_BLOCK')
344-
self.loops.pop()
349+
self.setups.pop()
345350
if node.else_:
346351
self.visit(node.else_)
347352
self.nextBlock(after)
@@ -350,7 +355,7 @@ def visitFor(self, node):
350355
start = self.newBlock()
351356
anchor = self.newBlock()
352357
after = self.newBlock()
353-
self.loops.push(start)
358+
self.setups.push((LOOP, start))
354359

355360
self.set_lineno(node)
356361
self.emit('SETUP_LOOP', after)
@@ -365,26 +370,44 @@ def visitFor(self, node):
365370
self.emit('JUMP_ABSOLUTE', start)
366371
self.nextBlock(anchor)
367372
self.emit('POP_BLOCK')
368-
self.loops.pop()
373+
self.setups.pop()
369374
if node.else_:
370375
self.visit(node.else_)
371376
self.nextBlock(after)
372377

373378
def visitBreak(self, node):
374-
if not self.loops:
379+
if not self.setups:
375380
raise SyntaxError, "'break' outside loop (%s, %d)" % \
376381
(self.filename, node.lineno)
377382
self.set_lineno(node)
378383
self.emit('BREAK_LOOP')
379384

380385
def visitContinue(self, node):
381-
if not self.loops:
386+
if not self.setups:
382387
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
383388
(self.filename, node.lineno)
384-
l = self.loops.top()
385-
self.set_lineno(node)
386-
self.emit('JUMP_ABSOLUTE', l)
387-
self.nextBlock()
389+
kind, block = self.setups.top()
390+
if kind == LOOP:
391+
self.set_lineno(node)
392+
self.emit('JUMP_ABSOLUTE', block)
393+
self.nextBlock()
394+
elif kind == EXCEPT or kind == TRY_FINALLY:
395+
self.set_lineno(node)
396+
# find the block that starts the loop
397+
top = len(self.setups)
398+
while top > 0:
399+
top = top - 1
400+
kind, loop_block = self.setups[top]
401+
if kind == LOOP:
402+
break
403+
if kind != LOOP:
404+
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
405+
(self.filename, node.lineno)
406+
self.emit('CONTINUE_LOOP', loop_block)
407+
self.nextBlock()
408+
elif kind == END_FINALLY:
409+
msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
410+
raise SyntaxError, msg % (self.filename, node.lineno)
388411

389412
def visitTest(self, node, jump):
390413
end = self.newBlock()
@@ -529,6 +552,7 @@ def visitRaise(self, node):
529552
self.emit('RAISE_VARARGS', n)
530553

531554
def visitTryExcept(self, node):
555+
body = self.newBlock()
532556
handlers = self.newBlock()
533557
end = self.newBlock()
534558
if node.else_:
@@ -537,9 +561,11 @@ def visitTryExcept(self, node):
537561
lElse = end
538562
self.set_lineno(node)
539563
self.emit('SETUP_EXCEPT', handlers)
540-
self.nextBlock()
564+
self.nextBlock(body)
565+
self.setups.push((EXCEPT, body))
541566
self.visit(node.body)
542567
self.emit('POP_BLOCK')
568+
self.setups.pop()
543569
self.emit('JUMP_FORWARD', lElse)
544570
self.startBlock(handlers)
545571

@@ -570,22 +596,28 @@ def visitTryExcept(self, node):
570596
if expr: # XXX
571597
self.emit('POP_TOP')
572598
self.emit('END_FINALLY')
599+
self.setups.pop()
573600
if node.else_:
574601
self.nextBlock(lElse)
575602
self.visit(node.else_)
576603
self.nextBlock(end)
577604

578605
def visitTryFinally(self, node):
606+
body = self.newBlock()
579607
final = self.newBlock()
580608
self.set_lineno(node)
581609
self.emit('SETUP_FINALLY', final)
582-
self.nextBlock()
610+
self.nextBlock(body)
611+
self.setups.push((TRY_FINALLY, body))
583612
self.visit(node.body)
584613
self.emit('POP_BLOCK')
614+
self.setups.pop()
585615
self.emit('LOAD_CONST', None)
586616
self.nextBlock(final)
617+
self.setups.push((END_FINALLY, final))
587618
self.visit(node.final)
588619
self.emit('END_FINALLY')
620+
self.setups.pop()
589621

590622
# misc
591623

Tools/compiler/compiler/pycodegen.py

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
(1,1) : "CALL_FUNCTION_VAR_KW",
2929
}
3030

31+
LOOP = 1
32+
EXCEPT = 2
33+
TRY_FINALLY = 3
34+
END_FINALLY = 4
35+
3136
def compile(filename, display=0):
3237
f = open(filename)
3338
buf = f.read()
@@ -142,7 +147,7 @@ def __init__(self, filename):
142147
self.checkClass()
143148
self.filename = filename
144149
self.locals = misc.Stack()
145-
self.loops = misc.Stack()
150+
self.setups = misc.Stack()
146151
self.curStack = 0
147152
self.maxStack = 0
148153
self.last_lineno = None
@@ -327,7 +332,7 @@ def visitWhile(self, node):
327332
self.emit('SETUP_LOOP', after)
328333

329334
self.nextBlock(loop)
330-
self.loops.push(loop)
335+
self.setups.push((LOOP, loop))
331336

332337
self.set_lineno(node, force=1)
333338
self.visit(node.test)
@@ -341,7 +346,7 @@ def visitWhile(self, node):
341346
self.startBlock(else_) # or just the POPs if not else clause
342347
self.emit('POP_TOP')
343348
self.emit('POP_BLOCK')
344-
self.loops.pop()
349+
self.setups.pop()
345350
if node.else_:
346351
self.visit(node.else_)
347352
self.nextBlock(after)
@@ -350,7 +355,7 @@ def visitFor(self, node):
350355
start = self.newBlock()
351356
anchor = self.newBlock()
352357
after = self.newBlock()
353-
self.loops.push(start)
358+
self.setups.push((LOOP, start))
354359

355360
self.set_lineno(node)
356361
self.emit('SETUP_LOOP', after)
@@ -365,26 +370,44 @@ def visitFor(self, node):
365370
self.emit('JUMP_ABSOLUTE', start)
366371
self.nextBlock(anchor)
367372
self.emit('POP_BLOCK')
368-
self.loops.pop()
373+
self.setups.pop()
369374
if node.else_:
370375
self.visit(node.else_)
371376
self.nextBlock(after)
372377

373378
def visitBreak(self, node):
374-
if not self.loops:
379+
if not self.setups:
375380
raise SyntaxError, "'break' outside loop (%s, %d)" % \
376381
(self.filename, node.lineno)
377382
self.set_lineno(node)
378383
self.emit('BREAK_LOOP')
379384

380385
def visitContinue(self, node):
381-
if not self.loops:
386+
if not self.setups:
382387
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
383388
(self.filename, node.lineno)
384-
l = self.loops.top()
385-
self.set_lineno(node)
386-
self.emit('JUMP_ABSOLUTE', l)
387-
self.nextBlock()
389+
kind, block = self.setups.top()
390+
if kind == LOOP:
391+
self.set_lineno(node)
392+
self.emit('JUMP_ABSOLUTE', block)
393+
self.nextBlock()
394+
elif kind == EXCEPT or kind == TRY_FINALLY:
395+
self.set_lineno(node)
396+
# find the block that starts the loop
397+
top = len(self.setups)
398+
while top > 0:
399+
top = top - 1
400+
kind, loop_block = self.setups[top]
401+
if kind == LOOP:
402+
break
403+
if kind != LOOP:
404+
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
405+
(self.filename, node.lineno)
406+
self.emit('CONTINUE_LOOP', loop_block)
407+
self.nextBlock()
408+
elif kind == END_FINALLY:
409+
msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
410+
raise SyntaxError, msg % (self.filename, node.lineno)
388411

389412
def visitTest(self, node, jump):
390413
end = self.newBlock()
@@ -529,6 +552,7 @@ def visitRaise(self, node):
529552
self.emit('RAISE_VARARGS', n)
530553

531554
def visitTryExcept(self, node):
555+
body = self.newBlock()
532556
handlers = self.newBlock()
533557
end = self.newBlock()
534558
if node.else_:
@@ -537,9 +561,11 @@ def visitTryExcept(self, node):
537561
lElse = end
538562
self.set_lineno(node)
539563
self.emit('SETUP_EXCEPT', handlers)
540-
self.nextBlock()
564+
self.nextBlock(body)
565+
self.setups.push((EXCEPT, body))
541566
self.visit(node.body)
542567
self.emit('POP_BLOCK')
568+
self.setups.pop()
543569
self.emit('JUMP_FORWARD', lElse)
544570
self.startBlock(handlers)
545571

@@ -570,22 +596,28 @@ def visitTryExcept(self, node):
570596
if expr: # XXX
571597
self.emit('POP_TOP')
572598
self.emit('END_FINALLY')
599+
self.setups.pop()
573600
if node.else_:
574601
self.nextBlock(lElse)
575602
self.visit(node.else_)
576603
self.nextBlock(end)
577604

578605
def visitTryFinally(self, node):
606+
body = self.newBlock()
579607
final = self.newBlock()
580608
self.set_lineno(node)
581609
self.emit('SETUP_FINALLY', final)
582-
self.nextBlock()
610+
self.nextBlock(body)
611+
self.setups.push((TRY_FINALLY, body))
583612
self.visit(node.body)
584613
self.emit('POP_BLOCK')
614+
self.setups.pop()
585615
self.emit('LOAD_CONST', None)
586616
self.nextBlock(final)
617+
self.setups.push((END_FINALLY, final))
587618
self.visit(node.final)
588619
self.emit('END_FINALLY')
620+
self.setups.pop()
589621

590622
# misc
591623

0 commit comments

Comments
 (0)