[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-svn
mailing list