[pypy-svn] r70784 - in pypy/trunk/pypy: jit/metainterp rlib rpython rpython/lltypesystem translator translator/c translator/c/src translator/c/test translator/cli translator/jvm translator/test

arigo at codespeak.net arigo at codespeak.net
Fri Jan 22 18:09:33 CET 2010


Author: arigo
Date: Fri Jan 22 18:09:32 2010
New Revision: 70784

Added:
   pypy/trunk/pypy/translator/c/src/debug_print.h
      - copied unchanged from r70783, pypy/branch/c-traceback/pypy/translator/c/src/debug_print.h
   pypy/trunk/pypy/translator/c/src/debug_traceback.h
      - copied unchanged from r70783, pypy/branch/c-traceback/pypy/translator/c/src/debug_traceback.h
Removed:
   pypy/trunk/pypy/translator/c/src/debug.h
   pypy/trunk/pypy/translator/c/src/debuginfo.h
Modified:
   pypy/trunk/pypy/jit/metainterp/history.py
   pypy/trunk/pypy/jit/metainterp/warmspot.py
   pypy/trunk/pypy/rlib/debug.py
   pypy/trunk/pypy/rpython/llinterp.py
   pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
   pypy/trunk/pypy/translator/c/funcgen.py
   pypy/trunk/pypy/translator/c/genc.py
   pypy/trunk/pypy/translator/c/src/g_include.h
   pypy/trunk/pypy/translator/c/src/main.h
   pypy/trunk/pypy/translator/c/test/test_standalone.py
   pypy/trunk/pypy/translator/cli/opcodes.py
   pypy/trunk/pypy/translator/exceptiontransform.py
   pypy/trunk/pypy/translator/jvm/opcodes.py
   pypy/trunk/pypy/translator/test/test_exceptiontransform.py
Log:
Merge branch/c-traceback.  It adds to all crashing RPython programs
a traceback capability that should let it display where the RPython
exception comes from.  (Only implemented in the C backend; I except
the oo backends to be already able to give such tracebacks.)


Modified: pypy/trunk/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/history.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/history.py	Fri Jan 22 18:09:32 2010
@@ -929,10 +929,6 @@
             loops.append(loop)
         display_loops(loops, errmsg, extraloops)
 
-
-class CrashInJIT(Exception):
-    pass
-
 # ----------------------------------------------------------------
 
 class Options:

Modified: pypy/trunk/pypy/jit/metainterp/warmspot.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/warmspot.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/warmspot.py	Fri Jan 22 18:09:32 2010
@@ -11,7 +11,7 @@
 from pypy.rlib.objectmodel import we_are_translated
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.rarithmetic import r_uint, intmask
-from pypy.rlib.debug import debug_print
+from pypy.rlib.debug import debug_print, fatalerror
 from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.translator.simplify import get_funcobj, get_functype
 from pypy.translator.unsimplify import call_final_function
@@ -344,9 +344,7 @@
                 if sys.stdout == sys.__stdout__:
                     import pdb; pdb.post_mortem(sys.exc_info()[2])
                 raise
-            debug_print('~~~ Crash in JIT!')
-            debug_print('~~~ %s' % (e,))
-            raise history.CrashInJIT("crash in JIT")
+            fatalerror('~~~ Crash in JIT! %s' % (e,), traceback=True)
         crash_in_jit._dont_inline_ = True
 
         if self.translator.rtyper.type_system.name == 'lltypesystem':

Modified: pypy/trunk/pypy/rlib/debug.py
==============================================================================
--- pypy/trunk/pypy/rlib/debug.py	(original)
+++ pypy/trunk/pypy/rlib/debug.py	Fri Jan 22 18:09:32 2010
@@ -19,6 +19,14 @@
         hop.exception_cannot_occur()
         hop.genop('debug_assert', vlist)
 
+def fatalerror(msg, traceback=False):
+    from pypy.rpython.lltypesystem import lltype
+    from pypy.rpython.lltypesystem.lloperation import llop
+    if traceback:
+        llop.debug_print_traceback(lltype.Void)
+    llop.debug_fatalerror(lltype.Void, msg)
+fatalerror._dont_inline_ = True
+
 
 class DebugLog(list):
     def debug_print(self, *args):

Modified: pypy/trunk/pypy/rpython/llinterp.py
==============================================================================
--- pypy/trunk/pypy/rpython/llinterp.py	(original)
+++ pypy/trunk/pypy/rpython/llinterp.py	Fri Jan 22 18:09:32 2010
@@ -543,6 +543,21 @@
     def op_debug_llinterpcall(self, pythonfunction, *args_ll):
         return pythonfunction(*args_ll)
 
+    def op_debug_start_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_reraise_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_record_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_print_traceback(self, *args):
+        pass    # xxx write debugging code here?
+
+    def op_debug_catch_exception(self, *args):
+        pass    # xxx write debugging code here?
+
     def op_jit_marker(self, *args):
         pass
 

Modified: pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lloperation.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lloperation.py	Fri Jan 22 18:09:32 2010
@@ -536,6 +536,11 @@
     'debug_fatalerror':     LLOp(),
     'debug_llinterpcall':   LLOp(), # Python func call 'res=arg[0](*arg[1:])'
                                     # in backends, abort() or whatever is fine
+    'debug_start_traceback':   LLOp(),
+    'debug_record_traceback':  LLOp(),
+    'debug_catch_exception':   LLOp(),
+    'debug_reraise_traceback': LLOp(),
+    'debug_print_traceback':   LLOp(),
 
     # __________ instrumentation _________
     'instrument_count':     LLOp(),

Modified: pypy/trunk/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/trunk/pypy/translator/c/funcgen.py	(original)
+++ pypy/trunk/pypy/translator/c/funcgen.py	Fri Jan 22 18:09:32 2010
@@ -817,5 +817,26 @@
             self.expr(op.args[1]),
             self.expr(op.args[2]))
 
+    def getdebugfunctionname(self):
+        name = self.functionname
+        if not name:
+            return "?"
+        if name.startswith('pypy_g_'):
+            name = name[7:]
+        return name
+
+    def OP_DEBUG_RECORD_TRACEBACK(self, op):
+        #if self.functionname is None, we print "?" as the argument */
+        return 'PYPY_DEBUG_RECORD_TRACEBACK("%s");' % (
+            self.getdebugfunctionname(),)
+
+    def OP_DEBUG_CATCH_EXCEPTION(self, op):
+        gottype = self.expr(op.args[0])
+        exprs = []
+        for c_limited_type in op.args[1:]:
+            exprs.append('%s == %s' % (gottype, self.expr(c_limited_type)))
+        return 'PYPY_DEBUG_CATCH_EXCEPTION("%s", %s, %s);' % (
+            self.getdebugfunctionname(), gottype, ' || '.join(exprs))
+
 
 assert not USESLOTS or '__dict__' not in dir(FunctionCodeGenerator)

Modified: pypy/trunk/pypy/translator/c/genc.py
==============================================================================
--- pypy/trunk/pypy/translator/c/genc.py	(original)
+++ pypy/trunk/pypy/translator/c/genc.py	Fri Jan 22 18:09:32 2010
@@ -430,12 +430,16 @@
         bk = self.translator.annotator.bookkeeper
         return getfunctionptr(bk.getdesc(self.entrypoint).getuniquegraph())
 
-    def cmdexec(self, args='', env=None, err=False):
+    def cmdexec(self, args='', env=None, err=False, expect_crash=False):
         assert self._compiled
         res = self.translator.platform.execute(self.executable_name, args,
                                                env=env)
         if res.returncode != 0:
+            if expect_crash:
+                return res.out, res.err
             raise Exception("Returned %d" % (res.returncode,))
+        if expect_crash:
+            raise Exception("Program did not crash!")
         if err:
             return res.out, res.err
         return res.out
@@ -711,6 +715,7 @@
             print >> fc, '/***  Implementations                                    ***/'
             print >> fc
             print >> fc, '#define PYPY_NOT_MAIN_FILE'
+            print >> fc, '#define PYPY_FILE_NAME "%s"' % name
             print >> fc, '#include "common_header.h"'
             print >> fc, '#include "structdef.h"'
             print >> fc, '#include "forwarddecl.h"'
@@ -783,6 +788,7 @@
     print >> f, '/***********************************************************/'
     print >> f, '/***  Implementations                                    ***/'
     print >> f
+    print >> f, '#define PYPY_FILE_NAME "%s"' % os.path.basename(f.name)
     for line in preimplementationlines:
         print >> f, line
     print >> f, '#include "src/g_include.h"'

Modified: pypy/trunk/pypy/translator/c/src/g_include.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/g_include.h	(original)
+++ pypy/trunk/pypy/translator/c/src/g_include.h	Fri Jan 22 18:09:32 2010
@@ -47,7 +47,8 @@
 /*** modules ***/
 #ifdef HAVE_RTYPER      /* only if we have an RTyper */
 #  include "src/rtyper.h"
-#  include "src/debug.h"
+#  include "src/debug_print.h"
+#  include "src/debug_traceback.h"
 #ifndef AVR
 #  include "src/ll_os.h"
 #  include "src/ll_strtod.h"

Modified: pypy/trunk/pypy/translator/c/src/main.h
==============================================================================
--- pypy/trunk/pypy/translator/c/src/main.h	(original)
+++ pypy/trunk/pypy/translator/c/src/main.h	Fri Jan 22 18:09:32 2010
@@ -36,12 +36,8 @@
 
     exitcode = STANDALONE_ENTRY_POINT(list);
     if (RPyExceptionOccurred()) {
-        /* fish for the exception type, at least */
-#ifndef AVR
-        fprintf(stderr, "Fatal RPython error: %s\n",
-                RPyFetchExceptionType()->ov_name->items);
-#endif
-        abort();
+        /* print the RPython traceback */
+        pypy_debug_catch_fatal_exception();
     }
     return exitcode;
 

Modified: pypy/trunk/pypy/translator/c/test/test_standalone.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_standalone.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_standalone.py	Fri Jan 22 18:09:32 2010
@@ -10,6 +10,7 @@
 from pypy.annotation.listdef import s_list_of_strings
 from pypy.tool.udir import udir
 from pypy.tool.autopath import pypydir
+from pypy.conftest import option
 
 
 class StandaloneTests(object):
@@ -21,8 +22,10 @@
         t.buildrtyper().specialize()
 
         cbuilder = CStandaloneBuilder(t, entry_point, t.config)
-        cbuilder.generate_source()
+        cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
         cbuilder.compile()
+        if option.view:
+            t.view()
         return t, cbuilder
 
 
@@ -377,6 +380,186 @@
         assert not err
         assert path.check(file=0)
 
+    def test_fatal_error(self):
+        def g(x):
+            if x == 1:
+                raise ValueError
+            else:
+                raise KeyError
+        def entry_point(argv):
+            if len(argv) < 3:
+                g(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: ValueError'
+        assert len(lines) >= 4
+        l0, l1, l2 = lines[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        #
+        out2, err2 = cbuilder.cmdexec("x", expect_crash=True)
+        assert out2.strip() == ''
+        lines2 = err2.strip().splitlines()
+        assert lines2[-1] == 'Fatal RPython error: KeyError'
+        l0, l1, l2 = lines2[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        assert lines2[-2] != lines[-2]    # different line number
+        assert lines2[-3] == lines[-3]    # same line number
+
+    def test_fatal_error_finally_1(self):
+        # a simple case of try:finally:
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == 'done.'
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: KeyError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l3)
+
+    def test_fatal_error_finally_2(self):
+        # a try:finally: in which we raise and catch another exception
+        def raiseme(x):
+            if x == 1:
+                raise ValueError
+        def raise_and_catch(x):
+            try:
+                raiseme(x)
+            except ValueError:
+                pass
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                raise_and_catch(x)
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == 'done.'
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: KeyError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l3)
+
+    def test_fatal_error_finally_3(self):
+        py.test.skip("not implemented: "
+                     "a try:finally: in which we raise the *same* exception")
+
+    def test_fatal_error_finally_4(self):
+        # a try:finally: in which we raise (and don't catch) an exception
+        def raiseme(x):
+            if x == 1:
+                raise ValueError
+        def g(x):
+            if x == 1:
+                raise KeyError
+        def h(x):
+            try:
+                g(x)
+            finally:
+                raiseme(x)
+                os.write(1, 'done.\n')
+        def entry_point(argv):
+            if len(argv) < 3:
+                h(len(argv))
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        #
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: ValueError'
+        assert len(lines) >= 5
+        l0, l1, l2, l3 = lines[-5:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in entry_point', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in h', l2)
+        assert re.match(r'  File "\w+.c", line \d+, in raiseme', l3)
+
+    def test_assertion_error(self):
+        def g(x):
+            assert x != 1
+        def f(argv):
+            try:
+                g(len(argv))
+            finally:
+                print 'done'
+        def entry_point(argv):
+            f(argv)
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'Fatal RPython error: AssertionError'
+        assert len(lines) >= 4
+        l0, l1, l2 = lines[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in f', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        # The traceback stops at f() because it's the first function that
+        # captures the AssertionError, which makes the program abort.
+
+    def test_ll_assert_error(self):
+        py.test.skip("implement later, maybe: tracebacks even with ll_assert")
+        def g(x):
+            ll_assert(x != 1, "foobar")
+        def f(argv):
+            try:
+                g(len(argv))
+            finally:
+                print 'done'
+        def entry_point(argv):
+            f(argv)
+            return 0
+        t, cbuilder = self.compile(entry_point)
+        out, err = cbuilder.cmdexec("", expect_crash=True)
+        assert out.strip() == ''
+        lines = err.strip().splitlines()
+        assert lines[-1] == 'PyPy assertion failed: foobar'
+        assert len(lines) >= 4
+        l0, l1, l2 = lines[-4:-1]
+        assert l0 == 'RPython traceback:'
+        assert re.match(r'  File "\w+.c", line \d+, in f', l1)
+        assert re.match(r'  File "\w+.c", line \d+, in g', l2)
+        # The traceback stops at f() because it's the first function that
+        # captures the AssertionError, which makes the program abort.
+
 
 class TestMaemo(TestStandalone):
     def setup_class(cls):
@@ -407,7 +590,7 @@
         t.buildrtyper().specialize()
         #
         cbuilder = CStandaloneBuilder(t, entry_point, t.config)
-        cbuilder.generate_source()
+        cbuilder.generate_source(defines=cbuilder.DEBUG_DEFINES)
         cbuilder.compile()
         #
         return t, cbuilder

Modified: pypy/trunk/pypy/translator/cli/opcodes.py
==============================================================================
--- pypy/trunk/pypy/translator/cli/opcodes.py	(original)
+++ pypy/trunk/pypy/translator/cli/opcodes.py	Fri Jan 22 18:09:32 2010
@@ -77,6 +77,11 @@
     'gc_set_max_heap_size':     Ignore,
     'resume_point':             Ignore,
     'debug_assert':             Ignore,
+    'debug_start_traceback':    Ignore,
+    'debug_record_traceback':   Ignore,
+    'debug_catch_exception':    Ignore,
+    'debug_reraise_traceback':  Ignore,
+    'debug_print_traceback':    Ignore,
     'debug_print':              [DebugPrint],
     'debug_start':              [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_START(string)'],
     'debug_stop':               [PushAllArgs, 'call void [pypylib]pypy.runtime.DebugPrint::DEBUG_STOP(string)'],

Modified: pypy/trunk/pypy/translator/exceptiontransform.py
==============================================================================
--- pypy/trunk/pypy/translator/exceptiontransform.py	(original)
+++ pypy/trunk/pypy/translator/exceptiontransform.py	Fri Jan 22 18:09:32 2010
@@ -3,11 +3,10 @@
 from pypy.translator.unsimplify import insert_empty_block, split_block
 from pypy.translator.backendopt import canraise, inline, support, removenoops
 from pypy.objspace.flow.model import Block, Constant, Variable, Link, \
-    c_last_exception, SpaceOperation, checkgraph, FunctionGraph
+    c_last_exception, SpaceOperation, checkgraph, FunctionGraph, mkentrymap
 from pypy.rpython.lltypesystem import lltype, llmemory, rffi
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.lltypesystem import lloperation
-from pypy.rpython.lltypesystem.llmemory import NULL
 from pypy.rpython import rtyper
 from pypy.rpython import rclass
 from pypy.rpython.rmodel import inputconst
@@ -16,6 +15,7 @@
 from pypy.rlib.debug import ll_assert
 from pypy.annotation import model as annmodel
 from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
+from pypy.tool.sourcetools import func_with_new_name
 
 PrimitiveErrorValue = {lltype.Signed: -1,
                        lltype.Unsigned: r_uint(-1),
@@ -26,7 +26,7 @@
                        lltype.Char: chr(255),
                        lltype.UniChar: unichr(0xFFFF), # XXX is this always right?
                        lltype.Bool: True,
-                       llmemory.Address: NULL,
+                       llmemory.Address: llmemory.NULL,
                        lltype.Void: None}
 
 for TYPE in rffi.NUMBER_TYPES:
@@ -45,6 +45,9 @@
 def error_constant(T):
     return Constant(error_value(T), T)
 
+def constant_value(llvalue):
+    return Constant(llvalue, lltype.typeOf(llvalue))
+
 class BaseExceptionTransformer(object):
 
     def __init__(self, translator):
@@ -64,6 +67,10 @@
         (n_i_error_ll_exc_type,
          n_i_error_ll_exc) = self.get_builtin_exception(NotImplementedError)
 
+        self.c_assertion_error_ll_exc_type = constant_value(
+            assertion_error_ll_exc_type)
+        self.c_n_i_error_ll_exc_type = constant_value(n_i_error_ll_exc_type)
+
         def rpyexc_occured():
             exc_type = exc_data.exc_type
             return bool(exc_type)
@@ -80,10 +87,14 @@
 
         def rpyexc_raise(etype, evalue):
             # assert(!RPyExceptionOccurred());
-            ll_assert(etype != assertion_error_ll_exc_type, "AssertionError!")
-            ll_assert(etype != n_i_error_ll_exc_type, "NotImplementedError!")
             exc_data.exc_type = etype
             exc_data.exc_value = evalue
+            lloperation.llop.debug_start_traceback(lltype.Void, etype)
+
+        def rpyexc_reraise(etype, evalue):
+            exc_data.exc_type = etype
+            exc_data.exc_value = evalue
+            lloperation.llop.debug_reraise_traceback(lltype.Void, etype)
 
         def rpyexc_fetch_exception():
             evalue = rpyexc_fetch_value()
@@ -92,7 +103,8 @@
         
         def rpyexc_restore_exception(evalue):
             if evalue:
-                rpyexc_raise(rclass.ll_inst_type(evalue), evalue)
+                exc_data.exc_type = rclass.ll_inst_type(evalue)
+                exc_data.exc_value = evalue
 
         def rpyexc_raise_runtime_error():
             rpyexc_raise(runtime_error_ll_exc_type, runtime_error_ll_exc)
@@ -119,14 +131,21 @@
 
         self.rpyexc_raise_ptr = self.build_func(
             "RPyRaiseException",
-            rpyexc_raise,
+            self.noinline(rpyexc_raise),
+            [self.lltype_of_exception_type, self.lltype_of_exception_value],
+            lltype.Void,
+            jitcallkind='rpyexc_raise') # for the JIT
+
+        self.rpyexc_reraise_ptr = self.build_func(
+            "RPyReRaiseException",
+            rpyexc_reraise,
             [self.lltype_of_exception_type, self.lltype_of_exception_value],
             lltype.Void,
             jitcallkind='rpyexc_raise') # for the JIT
 
         self.rpyexc_raise_runtime_error_ptr = self.build_func(
             "RPyRaiseRuntimeError",
-            rpyexc_raise_runtime_error,
+            self.noinline(rpyexc_raise_runtime_error),
             [], lltype.Void)
 
         self.rpyexc_fetch_exception_ptr = self.build_func(
@@ -136,7 +155,7 @@
 
         self.rpyexc_restore_exception_ptr = self.build_func(
             "RPyRestoreException",
-            rpyexc_restore_exception,
+            self.noinline(rpyexc_restore_exception),
             [self.lltype_of_exception_value], lltype.Void)
 
         self.build_extra_funcs()
@@ -144,6 +163,11 @@
         self.mixlevelannotator.finish()
         self.lltype_to_classdef = translator.rtyper.lltype_to_classdef_mapping()
 
+    def noinline(self, fn):
+        fn = func_with_new_name(fn, fn.__name__)
+        fn._dont_inline_ = True
+        return fn
+
     def build_func(self, name, fn, inputtypes, rettype, **kwds):
         l2a = annmodel.lltype_to_annotation
         graph = self.mixlevelannotator.getgraph(fn, map(l2a, inputtypes), l2a(rettype))
@@ -184,13 +208,18 @@
         # collect the blocks before changing them
         n_need_exc_matching_blocks = 0
         n_gen_exc_checks           = 0
+        #
+        entrymap = mkentrymap(graph)
+        if graph.exceptblock in entrymap:
+            for link in entrymap[graph.exceptblock]:
+                self.transform_jump_to_except_block(graph, entrymap, link)
+        #
         for block in list(graph.iterblocks()):
             self.replace_stack_unwind(block)
             self.replace_fetch_restore_operations(block)
             need_exc_matching, gen_exc_checks = self.transform_block(graph, block)
             n_need_exc_matching_blocks += need_exc_matching
             n_gen_exc_checks           += gen_exc_checks
-        self.transform_except_block(graph, graph.exceptblock)
         cleanup_graph(graph)
         removenoops.remove_superfluous_keep_alive(graph)
         return n_need_exc_matching_blocks, n_gen_exc_checks
@@ -268,18 +297,54 @@
                 self.insert_matching(lastblock, graph)
         return need_exc_matching, n_gen_exc_checks
 
-    def transform_except_block(self, graph, block):
-        # attach an except block -- let's hope that nobody uses it
-        graph.exceptblock = Block([Variable('etype'),   # exception class
-                                   Variable('evalue')])  # exception value
-        graph.exceptblock.operations = ()
-        graph.exceptblock.closeblock()
-        
+    def comes_from_last_exception(self, entrymap, link):
+        seen = {}
+        pending = [(link, link.args[1])]
+        while pending:
+            link, v = pending.pop()
+            if (link, v) in seen:
+                continue
+            seen[link, v] = True
+            if link.last_exc_value is not None and v is link.last_exc_value:
+                return True
+            block = link.prevblock
+            if block is None:
+                continue
+            for op in block.operations[::-1]:
+                if v is op.result:
+                    if op.opname == 'cast_pointer':
+                        v = op.args[0]
+                    else:
+                        break
+            for link in entrymap.get(block, ()):
+                for v1, v2 in zip(link.args, block.inputargs):
+                    if v2 is v:
+                        pending.append((link, v1))
+        return False
+
+    def transform_jump_to_except_block(self, graph, entrymap, link):
+        reraise = self.comes_from_last_exception(entrymap, link)
         result = Variable()
         result.concretetype = lltype.Void
-        block.operations = [SpaceOperation(
-           "direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result)]
-        l = Link([error_constant(graph.returnblock.inputargs[0].concretetype)], graph.returnblock)
+        block = Block([copyvar(None, v)
+                       for v in graph.exceptblock.inputargs])
+        if reraise:
+            block.operations = [
+                SpaceOperation("direct_call",
+                               [self.rpyexc_reraise_ptr] + block.inputargs,
+                               result),
+                ]
+        else:
+            block.operations = [
+                SpaceOperation("direct_call",
+                               [self.rpyexc_raise_ptr] + block.inputargs,
+                               result),
+                SpaceOperation('debug_record_traceback', [],
+                               varoftype(lltype.Void)),
+                ]
+        link.target = block
+        RETTYPE = graph.returnblock.inputargs[0].concretetype
+        l = Link([error_constant(RETTYPE)], graph.returnblock)
         block.recloseblock(l)
 
     def insert_matching(self, block, graph):
@@ -328,6 +393,11 @@
         llops = rtyper.LowLevelOpList(None)
         var_value = self.gen_getfield('exc_value', llops)
         var_type  = self.gen_getfield('exc_type' , llops)
+        #
+        c_check1 = self.c_assertion_error_ll_exc_type
+        c_check2 = self.c_n_i_error_ll_exc_type
+        llops.genop('debug_catch_exception', [var_type, c_check1, c_check2])
+        #
         self.gen_setfield('exc_value', self.c_null_evalue, llops)
         self.gen_setfield('exc_type',  self.c_null_etype,  llops)
         excblock.operations[:] = llops
@@ -361,7 +431,12 @@
         
         block.exitswitch = var_no_exc
         #exception occurred case
+        b = Block([])
+        b.operations = [SpaceOperation('debug_record_traceback', [],
+                                       varoftype(lltype.Void))]
         l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock)
+        b.closeblock(l)
+        l = Link([], b)
         l.exitcase = l.llexitcase = False
 
         #non-exception case

Modified: pypy/trunk/pypy/translator/jvm/opcodes.py
==============================================================================
--- pypy/trunk/pypy/translator/jvm/opcodes.py	(original)
+++ pypy/trunk/pypy/translator/jvm/opcodes.py	Fri Jan 22 18:09:32 2010
@@ -101,6 +101,11 @@
     'jit_force_virtual':        DoNothing,
 
     'debug_assert':              [], # TODO: implement?
+    'debug_start_traceback':    Ignore,
+    'debug_record_traceback':   Ignore,
+    'debug_catch_exception':    Ignore,
+    'debug_reraise_traceback':  Ignore,
+    'debug_print_traceback':    Ignore,
 
     # __________ numeric operations __________
 

Modified: pypy/trunk/pypy/translator/test/test_exceptiontransform.py
==============================================================================
--- pypy/trunk/pypy/translator/test/test_exceptiontransform.py	(original)
+++ pypy/trunk/pypy/translator/test/test_exceptiontransform.py	Fri Jan 22 18:09:32 2010
@@ -95,7 +95,7 @@
                 return 3 + x
             return 4 + x
         t, g = self.transform_func(foo, [int])
-        assert len(list(g.iterblocks())) == 9
+        assert len(list(g.iterblocks())) == 10
         f = self.compile(foo, [int])
         result = interpret(foo, [6])
         assert result == 2
@@ -126,7 +126,7 @@
                 return 1 + x
             return 4 + x
         t, g = self.transform_func(foo, [int])
-        assert len(list(g.iterblocks())) == 5
+        assert len(list(g.iterblocks())) == 6
         f = self.compile(foo, [int])
         result = interpret(foo, [6])
         assert result == 2
@@ -175,6 +175,34 @@
         etrafo.create_exception_handling(g)    
         assert etrafo.raise_analyzer.analyze_direct_call(g)
 
+    def test_reraise_is_not_raise(self):
+        def one(x):
+            if x == 1:
+                raise ValueError()
+            elif x == 2:
+                raise TypeError()
+            return x - 5
+        def foo(x):
+            try:
+                return one(x)
+            except ValueError:
+                return -42
+        t, g = self.transform_func(foo, [int])
+        for block in g.iterblocks():
+            for op in block.operations:
+                # the operation 'debug_record_traceback' should only show up
+                # in a normal raise, not in a reraise
+                assert op.opname != 'debug_record_traceback'
+        f = self.compile(foo, [int])
+        result = interpret(foo, [7])
+        assert result == 2
+        result = f(7)
+        assert result == 2
+        result = interpret(foo, [1])
+        assert result == -42
+        result = f(1)
+        assert result == -42
+
 
 class TestLLType(BaseTestExceptionTransform):
     type_system = 'lltype'



More information about the Pypy-commit mailing list