From: <pj...@us...> - 2008-12-09 21:56:55
|
Revision: 5724 http://jython.svn.sourceforge.net/jython/?rev=5724&view=rev Author: pjenvey Date: 2008-12-09 21:56:51 +0000 (Tue, 09 Dec 2008) Log Message: ----------- o allow overriding of ExceptionHandler's inline finally block o fix the with statement's normal finally/exit block so it also happens for non local gotos (e.g. return, continue) o modify test_with instead of adding test_with_jy to test for this case -- the lack of these tests is really a test bug IMO fixes #1194 pointed out by Terrence Cole Modified Paths: -------------- trunk/jython/Lib/test/test_with.py trunk/jython/src/org/python/compiler/CodeCompiler.java Modified: trunk/jython/Lib/test/test_with.py =================================================================== --- trunk/jython/Lib/test/test_with.py 2008-12-09 08:45:33 UTC (rev 5723) +++ trunk/jython/Lib/test/test_with.py 2008-12-09 21:56:51 UTC (rev 5724) @@ -506,59 +506,70 @@ self.assertRaises(GeneratorExit, shouldThrow) -class NonLocalFlowControlTestCase(unittest.TestCase): +class NonLocalFlowControlTestCase(unittest.TestCase, + ContextmanagerAssertionMixin): def testWithBreak(self): + mock = mock_contextmanager_generator() counter = 0 while True: counter += 1 - with mock_contextmanager_generator(): + with mock: counter += 10 break counter += 100 # Not reached self.assertEqual(counter, 11) + self.assertAfterWithManagerInvariantsNoError(mock) def testWithContinue(self): + mock = mock_contextmanager_generator() counter = 0 while True: counter += 1 if counter > 2: break - with mock_contextmanager_generator(): + with mock: counter += 10 continue counter += 100 # Not reached self.assertEqual(counter, 12) + self.assertAfterWithManagerInvariantsNoError(mock) def testWithReturn(self): + mock = mock_contextmanager_generator() def foo(): counter = 0 while True: counter += 1 - with mock_contextmanager_generator(): + with mock: counter += 10 return counter counter += 100 # Not reached self.assertEqual(foo(), 11) + self.assertAfterWithManagerInvariantsNoError(mock) def testWithYield(self): + mock = mock_contextmanager_generator() def gen(): - with mock_contextmanager_generator(): + with mock: yield 12 yield 13 x = list(gen()) self.assertEqual(x, [12, 13]) + self.assertAfterWithManagerInvariantsNoError(mock) def testWithRaise(self): + mock = mock_contextmanager_generator() counter = 0 try: counter += 1 - with mock_contextmanager_generator(): + with mock: counter += 10 raise RuntimeError counter += 100 # Not reached except RuntimeError: self.assertEqual(counter, 11) + self.assertAfterWithManagerInvariants(mock, sys.exc_info()) else: self.fail("Didn't raise RuntimeError") Modified: trunk/jython/src/org/python/compiler/CodeCompiler.java =================================================================== --- trunk/jython/src/org/python/compiler/CodeCompiler.java 2008-12-09 08:45:33 UTC (rev 5723) +++ trunk/jython/src/org/python/compiler/CodeCompiler.java 2008-12-09 21:56:51 UTC (rev 5724) @@ -1225,7 +1225,7 @@ // also exiting the try: portion of this particular finally } if (handler.isFinallyHandler()) { - suite(((TryFinally) handler.node).getInternalFinalbody()); + handler.finalBody(this); } } @@ -2245,15 +2245,14 @@ throw new ParseException("'with' will become a reserved keyword in Python 2.6", node); } - Label label_body_start = new Label(); - Label label_body_end = new Label(); - Label label_catch = new Label(); - Label label_finally = new Label(); - Label label_end = new Label(); + final Label label_body_start = new Label(); + final Label label_body_end = new Label(); + final Label label_catch = new Label(); + final Label label_end = new Label(); - Method getattr = Method.getMethod("org.python.core.PyObject __getattr__ (String)"); - Method call = Method.getMethod("org.python.core.PyObject __call__ ()"); - Method call3 = Method.getMethod("org.python.core.PyObject __call__ (org.python.core.PyObject,org.python.core.PyObject,org.python.core.PyObject)"); + final Method getattr = Method.getMethod("org.python.core.PyObject __getattr__ (String)"); + final Method call = Method.getMethod("org.python.core.PyObject __call__ ()"); + final Method call3 = Method.getMethod("org.python.core.PyObject __call__ (org.python.core.PyObject,org.python.core.PyObject,org.python.core.PyObject)"); // mgr = (EXPR) visit(node.getInternalContext_expr()); @@ -2261,7 +2260,7 @@ code.ldc("__exit__"); code.invokevirtual(Type.getType(PyObject.class).getInternalName(), getattr.getName(), getattr.getDescriptor()); - int __exit__ = code.getLocal("org/python/core/PyObject"); + final int __exit__ = code.getLocal("org/python/core/PyObject"); code.astore(__exit__); // value = mgr.__enter__() @@ -2272,11 +2271,34 @@ code.astore(value_tmp); // exc = True # not necessary, since we don't exec finally if exception + + // FINALLY (preparation) + // ordinarily with a finally, we need to duplicate the code. that's not the case + // here + // # The normal and non-local-goto cases are handled here + // if exc: # implicit + // exit(None, None, None) + ExceptionHandler normalExit = new ExceptionHandler() { + @Override + public boolean isFinallyHandler() { return true; } + + @Override + public void finalBody(CodeCompiler compiler) throws Exception { + compiler.code.aload(__exit__); + compiler.getNone(); + compiler.code.dup(); + compiler.code.dup(); + compiler.code.invokevirtual(Type.getType(PyObject.class).getInternalName(), + call3.getName(), call3.getDescriptor()); + compiler.code.pop(); + } + }; + exceptionHandlers.push(normalExit); + // try-catch block here - //code.trycatch(label_body_start, label_body_end, label_catch, "java/lang/Throwable"); ExceptionHandler handler = new ExceptionHandler(); + exceptionHandlers.push(handler); handler.exceptionStarts.addElement(label_body_start); - exceptionHandlers.push(handler); // VAR = value # Only if "as VAR" is present code.label(label_body_start); @@ -2285,13 +2307,22 @@ } code.freeLocal(value_tmp); - // BLOCK - suite(node.getInternalBody()); + // BLOCK + FINALLY if non-local-goto + Object blockResult = suite(node.getInternalBody()); + normalExit.bodyDone = true; exceptionHandlers.pop(); - code.goto_(label_finally); + exceptionHandlers.pop(); code.label(label_body_end); handler.exceptionEnds.addElement(label_body_end); + // FINALLY if *not* non-local-goto + if (blockResult == NoExit) { + // BLOCK would have generated FINALLY for us if it exited (due to a break, + // continue or return) + inlineFinally(normalExit); + code.goto_(label_end); + } + // CATCH code.label(label_catch); @@ -2322,29 +2353,12 @@ code.invokestatic("org/python/core/Py", "makeException", "()Lorg/python/core/PyException;"); code.checkcast("java/lang/Throwable"); code.athrow(); - code.freeLocal(ts_tmp); - - handler.addExceptionHandlers(label_catch); - // FINALLY - // ordinarily with a finally, we need to duplicate the code. that's not the case here - // # The normal and non-local-goto cases are handled here - // if exc: # implicit - // exit(None, None, None) - - code.label(label_finally); - - code.aload(__exit__); - getNone(); - code.dup(); - code.dup(); - code.invokevirtual(Type.getType(PyObject.class).getInternalName(), call3.getName(), call3.getDescriptor()); - code.pop(); - code.label(label_end); code.freeLocal(__exit__); + handler.addExceptionHandlers(label_catch); return null; } @@ -2405,5 +2419,11 @@ } } } + + public void finalBody(CodeCompiler compiler) throws Exception { + if (node instanceof TryFinally) { + suite(((TryFinally)node).getInternalFinalbody()); + } + } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |