[pypy-svn] r35741 - in pypy/dist/pypy/translator: cli cli/test oosupport

antocuni at codespeak.net antocuni at codespeak.net
Thu Dec 14 15:10:29 CET 2006


Author: antocuni
Date: Thu Dec 14 15:10:27 2006
New Revision: 35741

Modified:
   pypy/dist/pypy/translator/cli/function.py
   pypy/dist/pypy/translator/cli/ilgenerator.py
   pypy/dist/pypy/translator/cli/metavm.py
   pypy/dist/pypy/translator/cli/test/runtest.py
   pypy/dist/pypy/translator/cli/test/test_exception.py
   pypy/dist/pypy/translator/oosupport/function.py
Log:
Hack to make gencli not use native .NET exceptions for the RPython
ones. It is disabled by default.

We need a saner way to do this, it will be removed in the future.



Modified: pypy/dist/pypy/translator/cli/function.py
==============================================================================
--- pypy/dist/pypy/translator/cli/function.py	(original)
+++ pypy/dist/pypy/translator/cli/function.py	Thu Dec 14 15:10:27 2006
@@ -15,23 +15,9 @@
 from pypy.translator.cli.support import log
 from pypy.translator.cli.ilgenerator import CLIBaseGenerator
 
-class Function(OOFunction, Node, CLIBaseGenerator):
-
-    def __init__(self, *args, **kwargs):
-        OOFunction.__init__(self, *args, **kwargs)
-        self._set_args()
-        self._set_locals()
-                 
-    def _create_generator(self, ilasm):
-        return self # Function implements the Generator interface
-
-    def record_ll_meta_exc(self, ll_meta_exc):
-        # record the type only if it doesn't belong to a native_class
-        ll_exc = ll_meta_exc._inst.class_._INSTANCE
-        NATIVE_INSTANCE = ll_exc._hints.get('NATIVE_INSTANCE', None)
-        if NATIVE_INSTANCE is None:
-            OOFunction.record_ll_meta_exc(self, ll_meta_exc)
+USE_LAST = False
 
+class NativeExceptionHandler(object):
     def begin_try(self):
         self.ilasm.begin_try()
 
@@ -49,6 +35,67 @@
         self.ilasm.leave(target_label)
         self.ilasm.end_catch()
 
+    def render_raise_block(self, block):
+        exc = block.inputargs[1]
+        self.load(exc)
+        self.ilasm.opcode('throw')
+
+    def store_exception_and_link(self, link):
+        if self._is_raise_block(link.target):
+            # the exception value is on the stack, use it as the 2nd target arg
+            assert len(link.args) == 2
+            assert len(link.target.inputargs) == 2
+            self.store(link.target.inputargs[1])
+        else:
+            # the exception value is on the stack, store it in the proper place
+            if isinstance(link.last_exception, flowmodel.Variable):
+                self.ilasm.opcode('dup')
+                self.store(link.last_exc_value)                            
+                self.ilasm.get_field(('class Object_meta', 'Object', 'meta'))
+                self.store(link.last_exception)
+            else:
+                self.store(link.last_exc_value)
+            self._setup_link(link)
+
+class LastExceptionHandler(object):
+    catch_label_count = 0
+    in_try = False
+
+    def next_catch_label(self):
+        self.catch_label_count += 1
+        return self.catch_label()
+
+    def catch_label(self):
+        return '__catch_%d' % self.catch_label_count
+
+    def begin_try(self):
+        self.in_try = True
+        self.ilasm.opcode('// begin_try')
+
+    def end_try(self, target_label):
+        self.ilasm.opcode('ldsfld', 'object last_exception')
+        self.ilasm.opcode('brnull', target_label)
+        self.ilasm.opcode('// end try')
+        self.in_try = False
+
+    def begin_catch(self, llexitcase):
+        self.ilasm.label(self.catch_label())
+        ll_meta_exc = llexitcase
+        ll_exc = ll_meta_exc._inst.class_._INSTANCE
+        cts_exc = self.cts.lltype_to_cts(ll_exc, False)
+        self.ilasm.opcode('ldsfld', 'object last_exception')
+        self.isinstance(cts_exc)
+        self.ilasm.opcode('dup')
+        self.ilasm.opcode('brtrue.s', 6) # ??
+        self.ilasm.opcode('pop')
+        self.ilasm.opcode('br', self.next_catch_label())
+        # here is the target of the above brtrue.s
+        self.ilasm.opcode('ldnull')
+        self.ilasm.opcode('stsfld', 'object last_exception')
+
+    def end_catch(self, target_label):
+        self.ilasm.opcode('br', target_label)
+
     def store_exception_and_link(self, link):
         if self._is_raise_block(link.target):
             # the exception value is on the stack, use it as the 2nd target arg
@@ -66,6 +113,84 @@
                 self.store(link.last_exc_value)
             self._setup_link(link)
 
+    def render_raise_block(self, block):
+        exc = block.inputargs[1]
+        self.load(exc)
+        self.ilasm.opcode('stsfld', 'object last_exception')
+        if not self.return_block: # must be a void function
+            TYPE = self.graph.getreturnvar().concretetype
+            default = TYPE._defl()
+            if default is not None: # concretetype is Void
+                try:
+                    self.db.constant_generator.push_primitive_constant(self, TYPE, default)
+                except AssertionError:
+                    self.ilasm.opcode('ldnull') # :-(
+            self.ilasm.opcode('ret')
+        else:
+            self.ilasm.opcode('br', self._get_block_name(self.return_block))
+
+
+    def _render_op(self, op):
+        from pypy.rpython.ootypesystem import ootype        
+        instr_list = self.db.genoo.opcodes.get(op.opname, None)
+        assert instr_list is not None, 'Unknown opcode: %s ' % op
+        assert isinstance(instr_list, InstructionList)
+        instr_list.render(self.generator, op)
+        if op.opname in ('direct_call', 'oosend', 'indirect_call') and not self.in_try:
+            self._premature_return()
+
+    def _premature_return(self):
+        try:
+            return_block = self._get_block_name(self.graph.returnblock)
+        except KeyError:
+            self.ilasm.opcode('//premature return')
+            self.ilasm.opcode('ldsfld', 'object last_exception')
+            TYPE = self.graph.getreturnvar().concretetype
+            default = TYPE._defl()
+            if default is None: # concretetype is Void
+                self.ilasm.opcode('brfalse.s', 1)
+                self.ilasm.opcode('ret')
+            else:
+                self.ilasm.opcode('brfalse.s', 3) # ??
+                try:
+                    self.db.constant_generator.push_primitive_constant(self, TYPE, default)
+                except AssertionError:
+                    self.ilasm.opcode('ldnull') # :-(
+                self.ilasm.opcode('ret')
+        else:
+            self.ilasm.opcode('ldsfld', 'object last_exception')
+            self.ilasm.opcode('brtrue', return_block)
+
+
+if USE_LAST:
+    ExceptionHandler = LastExceptionHandler
+else:
+    ExceptionHandler = NativeExceptionHandler
+
+class Function(ExceptionHandler, OOFunction, Node, CLIBaseGenerator):
+
+    def next_catch_label(self):
+        self.catch_label_count += 1
+        return self.catch_label()
+
+    def catch_label(self):
+        return '__catch_%d' % self.catch_label_count
+
+    def __init__(self, *args, **kwargs):
+        OOFunction.__init__(self, *args, **kwargs)
+        self._set_args()
+        self._set_locals()
+                 
+    def _create_generator(self, ilasm):
+        return self # Function implements the Generator interface
+
+    def record_ll_meta_exc(self, ll_meta_exc):
+        # record the type only if it doesn't belong to a native_class
+        ll_exc = ll_meta_exc._inst.class_._INSTANCE
+        NATIVE_INSTANCE = ll_exc._hints.get('NATIVE_INSTANCE', None)
+        if NATIVE_INSTANCE is None:
+            OOFunction.record_ll_meta_exc(self, ll_meta_exc)
+
     def begin_render(self):
         returntype, returnvar = self.cts.llvar_to_cts(self.graph.getreturnvar())
         if self.is_method:
@@ -77,6 +202,13 @@
         self.ilasm.begin_function(self.name, args, returntype, self.is_entrypoint, meth_type)        
         self.ilasm.locals(self.locals)
 
+    def before_last_blocks(self):
+        # This is only executed when using LastExceptionHandler.
+        # Need to be deleted when we will use a saner approach.
+        if hasattr(self, 'catch_label'):
+            self.ilasm.label(self.catch_label())
+            self.ilasm.opcode('nop')
+
     def end_render(self):
         self.ilasm.end_function()
 
@@ -89,12 +221,6 @@
             self.load(return_var)
         self.ilasm.opcode('ret')
 
-    def render_raise_block(self, block):
-        exc = block.inputargs[1]
-        self.load(exc)
-        self.ilasm.opcode('throw')
-
-
     # Those parts of the generator interface that are function
     # specific
 

Modified: pypy/dist/pypy/translator/cli/ilgenerator.py
==============================================================================
--- pypy/dist/pypy/translator/cli/ilgenerator.py	(original)
+++ pypy/dist/pypy/translator/cli/ilgenerator.py	Thu Dec 14 15:10:27 2006
@@ -55,6 +55,7 @@
         self.code.writeline('.assembly extern mscorlib {}')
         self.code.writeline('.assembly extern pypylib {}')
         self.code.writeline('.assembly %s {}' % name)
+        self.code.writeline('.field static object last_exception') # XXX
 
     def close(self):
         self.out.close()

Modified: pypy/dist/pypy/translator/cli/metavm.py
==============================================================================
--- pypy/dist/pypy/translator/cli/metavm.py	(original)
+++ pypy/dist/pypy/translator/cli/metavm.py	Thu Dec 14 15:10:27 2006
@@ -154,6 +154,12 @@
         self.mapping = mapping
 
     def render(self, generator, op):
+        if hasattr(self, 'catch_label'):
+            self.render_last(generator, op)
+        else:
+            self.render_native(generator, op)
+
+    def render_native(self, generator, op):
         ilasm = generator.ilasm
         label = '__check_block_%d' % MapException.COUNT
         MapException.COUNT += 1
@@ -169,6 +175,25 @@
         ilasm.label(label)
         ilasm.opcode('nop')
 
+    def render_last(self, generator, op):
+        ilasm = generator.ilasm
+        stdflow = '__check_block_%d' % MapException.COUNT
+        MapException.COUNT += 1
+        premature_return = '__check_block_%d' % MapException.COUNT
+        MapException.COUNT += 1
+        ilasm.begin_try()
+        self.instr.render(generator, op)
+        ilasm.leave(stdflow)
+        ilasm.end_try()
+        for cli_exc, py_exc in self.mapping:
+            ilasm.begin_catch(cli_exc)
+            ilasm.new('instance void class %s::.ctor()' % py_exc)
+            ilasm.opcode('stsfld', 'object last_exception')
+            ilasm.leave(stdflow)
+            ilasm.end_catch()
+        ilasm.label(stdflow)
+        ilasm.opcode('nop')
+
 class _Box(MicroInstruction): 
     def render(self, generator, op):
         generator.load(op.args[0])

Modified: pypy/dist/pypy/translator/cli/test/runtest.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/runtest.py	(original)
+++ pypy/dist/pypy/translator/cli/test/runtest.py	Thu Dec 14 15:10:27 2006
@@ -58,6 +58,11 @@
     def render(self, ilasm):
         ilasm.begin_function('main', [('string[]', 'argv')], 'void', True, 'static')
 
+        RETURN_TYPE = self.graph.getreturnvar().concretetype
+        return_type = self.cts.lltype_to_cts(RETURN_TYPE)
+        if return_type != 'void':
+            ilasm.locals([(return_type, 'res')])
+
         if self.wrap_exceptions:
             ilasm.begin_try()
 
@@ -72,10 +77,12 @@
 
         # call the function and convert the result to a string containing a valid python expression
         ilasm.call(self.cts.graph_to_signature(self.graph))
-        TYPE = self.graph.getreturnvar().concretetype
-        format_object(TYPE, self.cts, ilasm)
-        ilasm.call('void class [mscorlib]System.Console::WriteLine(string)')
-        ilasm.leave('return')
+        if return_type != 'void':
+            ilasm.opcode('stloc', 'res')
+        if self.wrap_exceptions:
+            ilasm.leave('check_last_exception')
+        else:
+            ilasm.leave('print_result')
 
         if self.wrap_exceptions:
             ilasm.end_try()
@@ -90,6 +97,21 @@
                     ilasm.leave('return')
                 ilasm.end_catch()
 
+            ilasm.label('check_last_exception')
+            ilasm.opcode('ldsfld', 'object last_exception')
+            ilasm.opcode('brnull', 'print_result')
+            # there is a pending exception
+            ilasm.opcode('ldsfld', 'object last_exception')
+            ilasm.call('string class [pypylib]pypy.test.Result::FormatException(object)')
+            ilasm.call('void class [mscorlib]System.Console::WriteLine(string)')
+            ilasm.opcode('br', 'return')
+
+        ilasm.label('print_result')
+        if return_type != 'void':
+            ilasm.opcode('ldloc', 'res')
+        format_object(RETURN_TYPE, self.cts, ilasm)
+        ilasm.call('void class [mscorlib]System.Console::WriteLine(string)')
+
         ilasm.label('return')
         ilasm.opcode('ret')
         ilasm.end_function()
@@ -140,6 +162,8 @@
        t.view()
 
     t.buildrtyper(type_system="ootype").specialize()
+    
+    
     main_graph = t.graphs[0]
 
     if getoption('view'):

Modified: pypy/dist/pypy/translator/cli/test/test_exception.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/test_exception.py	(original)
+++ pypy/dist/pypy/translator/cli/test/test_exception.py	Thu Dec 14 15:10:27 2006
@@ -38,3 +38,20 @@
 
     def test_raise_prebuilt_and_catch_other(self):
         pass
+
+    def test_missing_return_block(self):
+        class Base:
+            def foo(self):
+                raise ValueError
+
+        class Derived(Base):
+            def foo(self):
+                return 42
+
+        def fn(x):
+            if x:
+                obj = Base()
+            else:
+                obj = Derived()
+            return obj.foo()
+        assert self.interpret(fn, [0]) == 42

Modified: pypy/dist/pypy/translator/oosupport/function.py
==============================================================================
--- pypy/dist/pypy/translator/oosupport/function.py	(original)
+++ pypy/dist/pypy/translator/oosupport/function.py	Thu Dec 14 15:10:27 2006
@@ -82,15 +82,16 @@
         graph = self.graph
         self.begin_render()
 
-        return_blocks = []
+        self.return_block = None
+        self.raise_block = None
         for block in graph.iterblocks():
             if self._is_return_block(block):
-                return_blocks.append(block)
+                self.return_block = block
+            elif self._is_raise_block(block):
+                self.raise_block = block
             else:
                 self.set_label(self._get_block_name(block))
-                if self._is_raise_block(block):
-                    self.render_raise_block(block)
-                elif self._is_exc_handling_block(block):
+                if self._is_exc_handling_block(block):
                     self.render_exc_handling_block(block)
                 else:
                     self.render_normal_block(block)
@@ -98,14 +99,23 @@
         # render return blocks at the end just to please the .NET
         # runtime that seems to need a return statement at the end of
         # the function
-        for block in return_blocks:
-            self.set_label(self._get_block_name(block))
-            self.render_return_block(block)
+
+        self.before_last_blocks()
+
+        if self.raise_block:
+            self.set_label(self._get_block_name(self.raise_block))
+            self.render_raise_block(self.raise_block)
+        if self.return_block:
+            self.set_label(self._get_block_name(self.return_block))
+            self.render_return_block(self.return_block)
 
         self.end_render()
         if not self.is_method:
             self.db.record_function(self.graph, self.name)
 
+    def before_last_blocks(self):
+        pass
+
     def render_exc_handling_block(self, block):
         # renders all ops but the last one
         for op in block.operations[:-1]:



More information about the Pypy-commit mailing list