diff options
author | Jeremy Hylton <jeremy@alum.mit.edu> | 2001-08-29 22:30:09 (GMT) |
---|---|---|
committer | Jeremy Hylton <jeremy@alum.mit.edu> | 2001-08-29 22:30:09 (GMT) |
commit | e4685ec57e74ec37f234907df45159bcd1899a25 (patch) | |
tree | 389d0a03ecaddcb9132404911c3752f54d7a6645 | |
parent | 9263848fa154ad2ba5288bf5adeda22d7c90f8a2 (diff) | |
download | cpython-e4685ec57e74ec37f234907df45159bcd1899a25.zip cpython-e4685ec57e74ec37f234907df45159bcd1899a25.tar.gz cpython-e4685ec57e74ec37f234907df45159bcd1899a25.tar.bz2 |
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.
-rw-r--r-- | Lib/compiler/pycodegen.py | 58 | ||||
-rw-r--r-- | Tools/compiler/compiler/pycodegen.py | 58 |
2 files changed, 90 insertions, 26 deletions
diff --git a/Lib/compiler/pycodegen.py b/Lib/compiler/pycodegen.py index 07c82f7..e55ee90 100644 --- a/Lib/compiler/pycodegen.py +++ b/Lib/compiler/pycodegen.py @@ -28,6 +28,11 @@ callfunc_opcode_info = { (1,1) : "CALL_FUNCTION_VAR_KW", } +LOOP = 1 +EXCEPT = 2 +TRY_FINALLY = 3 +END_FINALLY = 4 + def compile(filename, display=0): f = open(filename) buf = f.read() @@ -142,7 +147,7 @@ class CodeGenerator: self.checkClass() self.filename = filename self.locals = misc.Stack() - self.loops = misc.Stack() + self.setups = misc.Stack() self.curStack = 0 self.maxStack = 0 self.last_lineno = None @@ -327,7 +332,7 @@ class CodeGenerator: self.emit('SETUP_LOOP', after) self.nextBlock(loop) - self.loops.push(loop) + self.setups.push((LOOP, loop)) self.set_lineno(node, force=1) self.visit(node.test) @@ -341,7 +346,7 @@ class CodeGenerator: self.startBlock(else_) # or just the POPs if not else clause self.emit('POP_TOP') self.emit('POP_BLOCK') - self.loops.pop() + self.setups.pop() if node.else_: self.visit(node.else_) self.nextBlock(after) @@ -350,7 +355,7 @@ class CodeGenerator: start = self.newBlock() anchor = self.newBlock() after = self.newBlock() - self.loops.push(start) + self.setups.push((LOOP, start)) self.set_lineno(node) self.emit('SETUP_LOOP', after) @@ -365,26 +370,44 @@ class CodeGenerator: self.emit('JUMP_ABSOLUTE', start) self.nextBlock(anchor) self.emit('POP_BLOCK') - self.loops.pop() + self.setups.pop() if node.else_: self.visit(node.else_) self.nextBlock(after) def visitBreak(self, node): - if not self.loops: + if not self.setups: raise SyntaxError, "'break' outside loop (%s, %d)" % \ (self.filename, node.lineno) self.set_lineno(node) self.emit('BREAK_LOOP') def visitContinue(self, node): - if not self.loops: + if not self.setups: raise SyntaxError, "'continue' outside loop (%s, %d)" % \ (self.filename, node.lineno) - l = self.loops.top() - self.set_lineno(node) - self.emit('JUMP_ABSOLUTE', l) - self.nextBlock() + kind, block = self.setups.top() + if kind == LOOP: + self.set_lineno(node) + self.emit('JUMP_ABSOLUTE', block) + 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): end = self.newBlock() @@ -529,6 +552,7 @@ class CodeGenerator: self.emit('RAISE_VARARGS', n) def visitTryExcept(self, node): + body = self.newBlock() handlers = self.newBlock() end = self.newBlock() if node.else_: @@ -537,9 +561,11 @@ class CodeGenerator: lElse = end self.set_lineno(node) self.emit('SETUP_EXCEPT', handlers) - self.nextBlock() + self.nextBlock(body) + self.setups.push((EXCEPT, body)) self.visit(node.body) self.emit('POP_BLOCK') + self.setups.pop() self.emit('JUMP_FORWARD', lElse) self.startBlock(handlers) @@ -570,22 +596,28 @@ class CodeGenerator: if expr: # XXX self.emit('POP_TOP') self.emit('END_FINALLY') + self.setups.pop() if node.else_: self.nextBlock(lElse) self.visit(node.else_) self.nextBlock(end) def visitTryFinally(self, node): + body = self.newBlock() final = self.newBlock() self.set_lineno(node) self.emit('SETUP_FINALLY', final) - self.nextBlock() + self.nextBlock(body) + self.setups.push((TRY_FINALLY, body)) self.visit(node.body) self.emit('POP_BLOCK') + self.setups.pop() self.emit('LOAD_CONST', None) self.nextBlock(final) + self.setups.push((END_FINALLY, final)) self.visit(node.final) self.emit('END_FINALLY') + self.setups.pop() # misc diff --git a/Tools/compiler/compiler/pycodegen.py b/Tools/compiler/compiler/pycodegen.py index 07c82f7..e55ee90 100644 --- a/Tools/compiler/compiler/pycodegen.py +++ b/Tools/compiler/compiler/pycodegen.py @@ -28,6 +28,11 @@ callfunc_opcode_info = { (1,1) : "CALL_FUNCTION_VAR_KW", } +LOOP = 1 +EXCEPT = 2 +TRY_FINALLY = 3 +END_FINALLY = 4 + def compile(filename, display=0): f = open(filename) buf = f.read() @@ -142,7 +147,7 @@ class CodeGenerator: self.checkClass() self.filename = filename self.locals = misc.Stack() - self.loops = misc.Stack() + self.setups = misc.Stack() self.curStack = 0 self.maxStack = 0 self.last_lineno = None @@ -327,7 +332,7 @@ class CodeGenerator: self.emit('SETUP_LOOP', after) self.nextBlock(loop) - self.loops.push(loop) + self.setups.push((LOOP, loop)) self.set_lineno(node, force=1) self.visit(node.test) @@ -341,7 +346,7 @@ class CodeGenerator: self.startBlock(else_) # or just the POPs if not else clause self.emit('POP_TOP') self.emit('POP_BLOCK') - self.loops.pop() + self.setups.pop() if node.else_: self.visit(node.else_) self.nextBlock(after) @@ -350,7 +355,7 @@ class CodeGenerator: start = self.newBlock() anchor = self.newBlock() after = self.newBlock() - self.loops.push(start) + self.setups.push((LOOP, start)) self.set_lineno(node) self.emit('SETUP_LOOP', after) @@ -365,26 +370,44 @@ class CodeGenerator: self.emit('JUMP_ABSOLUTE', start) self.nextBlock(anchor) self.emit('POP_BLOCK') - self.loops.pop() + self.setups.pop() if node.else_: self.visit(node.else_) self.nextBlock(after) def visitBreak(self, node): - if not self.loops: + if not self.setups: raise SyntaxError, "'break' outside loop (%s, %d)" % \ (self.filename, node.lineno) self.set_lineno(node) self.emit('BREAK_LOOP') def visitContinue(self, node): - if not self.loops: + if not self.setups: raise SyntaxError, "'continue' outside loop (%s, %d)" % \ (self.filename, node.lineno) - l = self.loops.top() - self.set_lineno(node) - self.emit('JUMP_ABSOLUTE', l) - self.nextBlock() + kind, block = self.setups.top() + if kind == LOOP: + self.set_lineno(node) + self.emit('JUMP_ABSOLUTE', block) + 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): end = self.newBlock() @@ -529,6 +552,7 @@ class CodeGenerator: self.emit('RAISE_VARARGS', n) def visitTryExcept(self, node): + body = self.newBlock() handlers = self.newBlock() end = self.newBlock() if node.else_: @@ -537,9 +561,11 @@ class CodeGenerator: lElse = end self.set_lineno(node) self.emit('SETUP_EXCEPT', handlers) - self.nextBlock() + self.nextBlock(body) + self.setups.push((EXCEPT, body)) self.visit(node.body) self.emit('POP_BLOCK') + self.setups.pop() self.emit('JUMP_FORWARD', lElse) self.startBlock(handlers) @@ -570,22 +596,28 @@ class CodeGenerator: if expr: # XXX self.emit('POP_TOP') self.emit('END_FINALLY') + self.setups.pop() if node.else_: self.nextBlock(lElse) self.visit(node.else_) self.nextBlock(end) def visitTryFinally(self, node): + body = self.newBlock() final = self.newBlock() self.set_lineno(node) self.emit('SETUP_FINALLY', final) - self.nextBlock() + self.nextBlock(body) + self.setups.push((TRY_FINALLY, body)) self.visit(node.body) self.emit('POP_BLOCK') + self.setups.pop() self.emit('LOAD_CONST', None) self.nextBlock(final) + self.setups.push((END_FINALLY, final)) self.visit(node.final) self.emit('END_FINALLY') + self.setups.pop() # misc |