Commit 99f3c5a5 authored by Jeremy Hylton's avatar Jeremy Hylton

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.
parent 611c1a35
...@@ -28,6 +28,11 @@ callfunc_opcode_info = { ...@@ -28,6 +28,11 @@ callfunc_opcode_info = {
(1,1) : "CALL_FUNCTION_VAR_KW", (1,1) : "CALL_FUNCTION_VAR_KW",
} }
LOOP = 1
EXCEPT = 2
TRY_FINALLY = 3
END_FINALLY = 4
def compile(filename, display=0): def compile(filename, display=0):
f = open(filename) f = open(filename)
buf = f.read() buf = f.read()
...@@ -142,7 +147,7 @@ class CodeGenerator: ...@@ -142,7 +147,7 @@ class CodeGenerator:
self.checkClass() self.checkClass()
self.filename = filename self.filename = filename
self.locals = misc.Stack() self.locals = misc.Stack()
self.loops = misc.Stack() self.setups = misc.Stack()
self.curStack = 0 self.curStack = 0
self.maxStack = 0 self.maxStack = 0
self.last_lineno = None self.last_lineno = None
...@@ -327,7 +332,7 @@ class CodeGenerator: ...@@ -327,7 +332,7 @@ class CodeGenerator:
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
self.nextBlock(loop) self.nextBlock(loop)
self.loops.push(loop) self.setups.push((LOOP, loop))
self.set_lineno(node, force=1) self.set_lineno(node, force=1)
self.visit(node.test) self.visit(node.test)
...@@ -341,7 +346,7 @@ class CodeGenerator: ...@@ -341,7 +346,7 @@ class CodeGenerator:
self.startBlock(else_) # or just the POPs if not else clause self.startBlock(else_) # or just the POPs if not else clause
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.loops.pop() self.setups.pop()
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(after) self.nextBlock(after)
...@@ -350,7 +355,7 @@ class CodeGenerator: ...@@ -350,7 +355,7 @@ class CodeGenerator:
start = self.newBlock() start = self.newBlock()
anchor = self.newBlock() anchor = self.newBlock()
after = self.newBlock() after = self.newBlock()
self.loops.push(start) self.setups.push((LOOP, start))
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
...@@ -365,26 +370,44 @@ class CodeGenerator: ...@@ -365,26 +370,44 @@ class CodeGenerator:
self.emit('JUMP_ABSOLUTE', start) self.emit('JUMP_ABSOLUTE', start)
self.nextBlock(anchor) self.nextBlock(anchor)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.loops.pop() self.setups.pop()
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(after) self.nextBlock(after)
def visitBreak(self, node): def visitBreak(self, node):
if not self.loops: if not self.setups:
raise SyntaxError, "'break' outside loop (%s, %d)" % \ raise SyntaxError, "'break' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
self.set_lineno(node) self.set_lineno(node)
self.emit('BREAK_LOOP') self.emit('BREAK_LOOP')
def visitContinue(self, node): def visitContinue(self, node):
if not self.loops: if not self.setups:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \ raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
l = self.loops.top() kind, block = self.setups.top()
if kind == LOOP:
self.set_lineno(node) self.set_lineno(node)
self.emit('JUMP_ABSOLUTE', l) self.emit('JUMP_ABSOLUTE', block)
self.nextBlock() self.nextBlock()
elif kind == EXCEPT or kind == TRY_FINALLY:
self.set_lineno(node)
# find the block that starts the loop
top = len(self.setups)
while top > 0:
top = top - 1
kind, loop_block = self.setups[top]
if kind == LOOP:
break
if kind != LOOP:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno)
self.emit('CONTINUE_LOOP', loop_block)
self.nextBlock()
elif kind == END_FINALLY:
msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
raise SyntaxError, msg % (self.filename, node.lineno)
def visitTest(self, node, jump): def visitTest(self, node, jump):
end = self.newBlock() end = self.newBlock()
...@@ -529,6 +552,7 @@ class CodeGenerator: ...@@ -529,6 +552,7 @@ class CodeGenerator:
self.emit('RAISE_VARARGS', n) self.emit('RAISE_VARARGS', n)
def visitTryExcept(self, node): def visitTryExcept(self, node):
body = self.newBlock()
handlers = self.newBlock() handlers = self.newBlock()
end = self.newBlock() end = self.newBlock()
if node.else_: if node.else_:
...@@ -537,9 +561,11 @@ class CodeGenerator: ...@@ -537,9 +561,11 @@ class CodeGenerator:
lElse = end lElse = end
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_EXCEPT', handlers) self.emit('SETUP_EXCEPT', handlers)
self.nextBlock() self.nextBlock(body)
self.setups.push((EXCEPT, body))
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.setups.pop()
self.emit('JUMP_FORWARD', lElse) self.emit('JUMP_FORWARD', lElse)
self.startBlock(handlers) self.startBlock(handlers)
...@@ -570,22 +596,28 @@ class CodeGenerator: ...@@ -570,22 +596,28 @@ class CodeGenerator:
if expr: # XXX if expr: # XXX
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('END_FINALLY') self.emit('END_FINALLY')
self.setups.pop()
if node.else_: if node.else_:
self.nextBlock(lElse) self.nextBlock(lElse)
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(end) self.nextBlock(end)
def visitTryFinally(self, node): def visitTryFinally(self, node):
body = self.newBlock()
final = self.newBlock() final = self.newBlock()
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_FINALLY', final) self.emit('SETUP_FINALLY', final)
self.nextBlock() self.nextBlock(body)
self.setups.push((TRY_FINALLY, body))
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.setups.pop()
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.nextBlock(final) self.nextBlock(final)
self.setups.push((END_FINALLY, final))
self.visit(node.final) self.visit(node.final)
self.emit('END_FINALLY') self.emit('END_FINALLY')
self.setups.pop()
# misc # misc
......
...@@ -28,6 +28,11 @@ callfunc_opcode_info = { ...@@ -28,6 +28,11 @@ callfunc_opcode_info = {
(1,1) : "CALL_FUNCTION_VAR_KW", (1,1) : "CALL_FUNCTION_VAR_KW",
} }
LOOP = 1
EXCEPT = 2
TRY_FINALLY = 3
END_FINALLY = 4
def compile(filename, display=0): def compile(filename, display=0):
f = open(filename) f = open(filename)
buf = f.read() buf = f.read()
...@@ -142,7 +147,7 @@ class CodeGenerator: ...@@ -142,7 +147,7 @@ class CodeGenerator:
self.checkClass() self.checkClass()
self.filename = filename self.filename = filename
self.locals = misc.Stack() self.locals = misc.Stack()
self.loops = misc.Stack() self.setups = misc.Stack()
self.curStack = 0 self.curStack = 0
self.maxStack = 0 self.maxStack = 0
self.last_lineno = None self.last_lineno = None
...@@ -327,7 +332,7 @@ class CodeGenerator: ...@@ -327,7 +332,7 @@ class CodeGenerator:
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
self.nextBlock(loop) self.nextBlock(loop)
self.loops.push(loop) self.setups.push((LOOP, loop))
self.set_lineno(node, force=1) self.set_lineno(node, force=1)
self.visit(node.test) self.visit(node.test)
...@@ -341,7 +346,7 @@ class CodeGenerator: ...@@ -341,7 +346,7 @@ class CodeGenerator:
self.startBlock(else_) # or just the POPs if not else clause self.startBlock(else_) # or just the POPs if not else clause
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.loops.pop() self.setups.pop()
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(after) self.nextBlock(after)
...@@ -350,7 +355,7 @@ class CodeGenerator: ...@@ -350,7 +355,7 @@ class CodeGenerator:
start = self.newBlock() start = self.newBlock()
anchor = self.newBlock() anchor = self.newBlock()
after = self.newBlock() after = self.newBlock()
self.loops.push(start) self.setups.push((LOOP, start))
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_LOOP', after) self.emit('SETUP_LOOP', after)
...@@ -365,26 +370,44 @@ class CodeGenerator: ...@@ -365,26 +370,44 @@ class CodeGenerator:
self.emit('JUMP_ABSOLUTE', start) self.emit('JUMP_ABSOLUTE', start)
self.nextBlock(anchor) self.nextBlock(anchor)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.loops.pop() self.setups.pop()
if node.else_: if node.else_:
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(after) self.nextBlock(after)
def visitBreak(self, node): def visitBreak(self, node):
if not self.loops: if not self.setups:
raise SyntaxError, "'break' outside loop (%s, %d)" % \ raise SyntaxError, "'break' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
self.set_lineno(node) self.set_lineno(node)
self.emit('BREAK_LOOP') self.emit('BREAK_LOOP')
def visitContinue(self, node): def visitContinue(self, node):
if not self.loops: if not self.setups:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \ raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno) (self.filename, node.lineno)
l = self.loops.top() kind, block = self.setups.top()
if kind == LOOP:
self.set_lineno(node) self.set_lineno(node)
self.emit('JUMP_ABSOLUTE', l) self.emit('JUMP_ABSOLUTE', block)
self.nextBlock() self.nextBlock()
elif kind == EXCEPT or kind == TRY_FINALLY:
self.set_lineno(node)
# find the block that starts the loop
top = len(self.setups)
while top > 0:
top = top - 1
kind, loop_block = self.setups[top]
if kind == LOOP:
break
if kind != LOOP:
raise SyntaxError, "'continue' outside loop (%s, %d)" % \
(self.filename, node.lineno)
self.emit('CONTINUE_LOOP', loop_block)
self.nextBlock()
elif kind == END_FINALLY:
msg = "'continue' not allowed inside 'finally' clause (%s, %d)"
raise SyntaxError, msg % (self.filename, node.lineno)
def visitTest(self, node, jump): def visitTest(self, node, jump):
end = self.newBlock() end = self.newBlock()
...@@ -529,6 +552,7 @@ class CodeGenerator: ...@@ -529,6 +552,7 @@ class CodeGenerator:
self.emit('RAISE_VARARGS', n) self.emit('RAISE_VARARGS', n)
def visitTryExcept(self, node): def visitTryExcept(self, node):
body = self.newBlock()
handlers = self.newBlock() handlers = self.newBlock()
end = self.newBlock() end = self.newBlock()
if node.else_: if node.else_:
...@@ -537,9 +561,11 @@ class CodeGenerator: ...@@ -537,9 +561,11 @@ class CodeGenerator:
lElse = end lElse = end
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_EXCEPT', handlers) self.emit('SETUP_EXCEPT', handlers)
self.nextBlock() self.nextBlock(body)
self.setups.push((EXCEPT, body))
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.setups.pop()
self.emit('JUMP_FORWARD', lElse) self.emit('JUMP_FORWARD', lElse)
self.startBlock(handlers) self.startBlock(handlers)
...@@ -570,22 +596,28 @@ class CodeGenerator: ...@@ -570,22 +596,28 @@ class CodeGenerator:
if expr: # XXX if expr: # XXX
self.emit('POP_TOP') self.emit('POP_TOP')
self.emit('END_FINALLY') self.emit('END_FINALLY')
self.setups.pop()
if node.else_: if node.else_:
self.nextBlock(lElse) self.nextBlock(lElse)
self.visit(node.else_) self.visit(node.else_)
self.nextBlock(end) self.nextBlock(end)
def visitTryFinally(self, node): def visitTryFinally(self, node):
body = self.newBlock()
final = self.newBlock() final = self.newBlock()
self.set_lineno(node) self.set_lineno(node)
self.emit('SETUP_FINALLY', final) self.emit('SETUP_FINALLY', final)
self.nextBlock() self.nextBlock(body)
self.setups.push((TRY_FINALLY, body))
self.visit(node.body) self.visit(node.body)
self.emit('POP_BLOCK') self.emit('POP_BLOCK')
self.setups.pop()
self.emit('LOAD_CONST', None) self.emit('LOAD_CONST', None)
self.nextBlock(final) self.nextBlock(final)
self.setups.push((END_FINALLY, final))
self.visit(node.final) self.visit(node.final)
self.emit('END_FINALLY') self.emit('END_FINALLY')
self.setups.pop()
# misc # misc
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment