[pypy-svn] r13327 - in pypy/dist/pypy: objspace/flow rpython rpython/test

arigo at codespeak.net arigo at codespeak.net
Mon Jun 13 01:33:24 CEST 2005


Author: arigo
Date: Mon Jun 13 01:33:20 2005
New Revision: 13327

Added:
   pypy/dist/pypy/rpython/exceptiondata.py   (contents, props changed)
Modified:
   pypy/dist/pypy/objspace/flow/model.py
   pypy/dist/pypy/rpython/rclass.py
   pypy/dist/pypy/rpython/rptr.py
   pypy/dist/pypy/rpython/rtyper.py
   pypy/dist/pypy/rpython/test/test_exception.py
Log:
Bordering on the insane, here is the start of rtyper support for exceptions,
including converting from an exception set by CPython to an instance of the
corresponding RPython class.  More general fun with strange code all around
the place.


Modified: pypy/dist/pypy/objspace/flow/model.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/model.py	(original)
+++ pypy/dist/pypy/objspace/flow/model.py	Mon Jun 13 01:33:20 2005
@@ -75,7 +75,7 @@
 
 class Link(object):
 
-    __slots__ = """args target exitcase prevblock
+    __slots__ = """args target exitcase llexitcase prevblock
                 last_exception last_exc_value""".split()
 
     def __init__(self, args, target, exitcase=None):

Added: pypy/dist/pypy/rpython/exceptiondata.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/exceptiondata.py	Mon Jun 13 01:33:20 2005
@@ -0,0 +1,108 @@
+from pypy.annotation import model as annmodel
+from pypy.rpython import rclass
+from pypy.rpython.annlowlevel import annotate_lowlevel_helper
+from pypy.rpython.lltype import *
+
+
+class ExceptionData:
+    """Public information for the code generators to help with exceptions."""
+
+    def __init__(self, rtyper):
+        # (NB. rclass identifies 'Exception' and 'object')
+        r_type = rclass.getclassrepr(rtyper, None)
+        r_instance = rclass.getinstancerepr(rtyper, None)
+        r_type.setup()
+        r_instance.setup()
+        self.lltype_of_exception_type  = r_type.lowleveltype
+        self.lltype_of_exception_value = r_instance.lowleveltype
+
+        # create helper functions
+        self.ll_exception_match  = self.make_exception_matcher(rtyper)
+        self.ll_type_of_exc_inst = self.make_type_of_exc_inst(rtyper)
+        self.ll_pyexcclass2exc   = self.make_pyexcclass2exc(rtyper)
+
+
+    def make_exception_matcher(self, rtyper):
+        # ll_exception_matcher(real_exception_vtable, match_exception_vtable)
+        s_typeptr = annmodel.SomePtr(self.lltype_of_exception_type)
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, rclass.ll_issubclass, [s_typeptr, s_typeptr])
+        return spec_function
+
+
+    def make_type_of_exc_inst(self, rtyper):
+        # ll_type_of_exc_inst(exception_instance) -> exception_vtable
+        s_excinst = annmodel.SomePtr(self.lltype_of_exception_value)
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, rclass.ll_type, [s_excinst])
+        return spec_function
+
+
+    def make_pyexcclass2exc(self, rtyper):
+        # ll_pyexcclass2exc(python_exception_class) -> exception_instance
+        table = {}
+        for clsdef in rtyper.class_reprs:
+            if (clsdef and clsdef.cls is not Exception
+                and issubclass(clsdef.cls, Exception)):
+                cls = clsdef.cls
+                if not clsdef.attrs:
+                    r_inst = rclass.getinstancerepr(rtyper, clsdef)
+                    r_inst.setup()
+                    example = malloc(r_inst.lowleveltype.TO, immortal=True)
+                    example = rclass.ll_cast_to_object(example)
+                    example.typeptr = r_inst.rclass.getvtable()
+                    table[cls] = example
+                else:
+                    assert cls.__module__ != 'exceptions', (
+                        "built-in exceptions should not grow attributes")
+        r_inst = rclass.getinstancerepr(rtyper, None)
+        r_inst.setup()
+        default_excinst = malloc(self.lltype_of_exception_value.TO,
+                                 immortal=True)
+        default_excinst.typeptr = r_inst.rclass.getvtable()
+
+        # build the table in order base classes first, subclasses last
+        sortedtable = []
+        def add_class(cls):
+            if cls in table:
+                for base in cls.__bases__:
+                    add_class(base)
+                sortedtable.append((cls, table[cls]))
+                del table[cls]
+        for cls in table.keys():
+            add_class(cls)
+        assert table == {}
+        print sortedtable
+
+        A = Array(('pycls', Ptr(PyObject)),
+                  ('excinst', self.lltype_of_exception_value))
+        pycls2excinst = malloc(A, len(sortedtable), immortal=True)
+        for i in range(len(sortedtable)):
+            cls, example = sortedtable[i]
+            pycls2excinst[i].pycls   = pyobjectptr(cls)
+            pycls2excinst[i].excinst = example
+
+        FUNCTYPE = FuncType([Ptr(PyObject), Ptr(PyObject)], Signed)
+        PyErr_GivenExceptionMatches = functionptr(
+            FUNCTYPE, "PyErr_GivenExceptionMatches", external="C",
+            _callable=lambda pyobj1, pyobj2:
+                          int(issubclass(pyobj1._obj.value, pyobj2._obj.value)))
+
+        initial_value_of_i = len(pycls2excinst)-1
+
+        def ll_pyexcclass2exc(python_exception_class):
+            """Return an RPython instance of the best approximation of the
+            Python exception identified by its Python class.
+            """
+            i = initial_value_of_i
+            while i >= 0:
+                if PyErr_GivenExceptionMatches(python_exception_class,
+                                               pycls2excinst[i].pycls):
+                    return pycls2excinst[i].excinst
+                i -= 1
+            return default_excinst
+
+        s_pyobj = annmodel.SomePtr(Ptr(PyObject))
+        dontcare, spec_function = annotate_lowlevel_helper(
+            rtyper.annotator, ll_pyexcclass2exc, [s_pyobj])
+        return spec_function

Modified: pypy/dist/pypy/rpython/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/rclass.py	(original)
+++ pypy/dist/pypy/rpython/rclass.py	Mon Jun 13 01:33:20 2005
@@ -37,20 +37,36 @@
 OBJECT_VTABLE.become(Struct('object_vtable', ('parenttypeptr', TYPEPTR)))
 
 OBJECT = GcStruct('object', ('typeptr', TYPEPTR))
-
+OBJECTPTR = Ptr(OBJECT)
 
 def getclassrepr(rtyper, classdef):
     try:
         result = rtyper.class_reprs[classdef]
     except KeyError:
-        result = rtyper.class_reprs[classdef] = ClassRepr(rtyper, classdef)
+        if classdef and classdef.cls is Exception:
+            # skip Exception as a base class and go directly to 'object'.
+            # the goal is to allow any class anywhere in the hierarchy
+            # to have Exception as a second base class.  It should be an
+            # empty class anyway.
+            if classdef.attrs:
+                raise TyperError("the Exception class should not "
+                                 "have any attribute attached to it")
+            result = getclassrepr(rtyper, None)
+        else:
+            result = ClassRepr(rtyper, classdef)
+        rtyper.class_reprs[classdef] = result
     return result
 
 def getinstancerepr(rtyper, classdef):
     try:
         result = rtyper.instance_reprs[classdef]
     except KeyError:
-        result = rtyper.instance_reprs[classdef] = InstanceRepr(rtyper,classdef)
+        if classdef and classdef.cls is Exception:
+            # see getclassrepr()
+            result = getinstancerepr(rtyper, None)
+        else:
+            result = InstanceRepr(rtyper,classdef)
+        rtyper.instance_reprs[classdef] = result
     return result
 
 class MissingRTypeAttribute(TyperError):
@@ -467,3 +483,25 @@
     classdef = hop.rtyper.annotator.getuserclasses()[cls]
     rinstance = getinstancerepr(hop.rtyper, classdef)
     return rinstance.new_instance(hop.llops)
+
+# ____________________________________________________________
+#
+#  Low-level implementation of operations on classes and instances
+
+def ll_cast_to_object(obj):
+    # This strange recursive version is type-safe :-)
+    # Each ll_cast_to_object() call below is done with a different type.
+    if typeOf(obj) == OBJECTPTR:
+        return obj
+    else:
+        return ll_cast_to_object(obj.super)
+
+def ll_type(obj):
+    return ll_cast_to_object(obj).typeptr
+
+def ll_issubclass(subcls, cls):
+    while subcls != cls:
+        if not subcls:
+            return False
+        subcls = subcls.parenttypeptr
+    return True

Modified: pypy/dist/pypy/rpython/rptr.py
==============================================================================
--- pypy/dist/pypy/rpython/rptr.py	(original)
+++ pypy/dist/pypy/rpython/rptr.py	Mon Jun 13 01:33:20 2005
@@ -1,18 +1,18 @@
 from pypy.annotation.pairtype import pairtype
 from pypy.annotation import model as annmodel
 from pypy.rpython.lltype import Ptr, _ptr
-from pypy.rpython.lltype import ContainerType, Void, Signed, Bool
+from pypy.rpython.lltype import ContainerType, Void, Signed, Bool, FuncType
 from pypy.rpython.rmodel import Repr, TyperError, IntegerRepr, inputconst
 
 
 class __extend__(annmodel.SomePtr):
     def rtyper_makerepr(self, rtyper):
-        if self.is_constant():   # constant NULL
+        if self.is_constant() and not self.const:   # constant NULL
             return nullptr_repr
         else:
             return PtrRepr(self.ll_ptrtype)
     def rtyper_makekey(self):
-        if self.is_constant():
+        if self.is_constant() and not self.const:
             return None
         else:
             return self.ll_ptrtype
@@ -51,6 +51,13 @@
         vlist = hop.inputargs(self)
         return hop.genop('ptr_nonzero', vlist, resulttype=Bool)
 
+    def rtype_simple_call(self, hop):
+        if not isinstance(self.lowleveltype.TO, FuncType):
+            raise TyperError("calling a non-function %r", self.lowleveltype.TO)
+        vlist = hop.inputargs(*hop.args_r)
+        return hop.genop('direct_call', vlist,
+                         resulttype = self.lowleveltype.TO.RESULT)
+
 
 class __extend__(pairtype(PtrRepr, IntegerRepr)):
 

Modified: pypy/dist/pypy/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py	(original)
+++ pypy/dist/pypy/rpython/rtyper.py	Mon Jun 13 01:33:20 2005
@@ -2,7 +2,7 @@
 from pypy.annotation.pairtype import pair
 from pypy.annotation import model as annmodel
 from pypy.objspace.flow.model import Variable, Constant, Block, Link
-from pypy.objspace.flow.model import SpaceOperation
+from pypy.objspace.flow.model import SpaceOperation, last_exception
 from pypy.rpython.lltype import Signed, Unsigned, Float, Char, Bool, Void
 from pypy.rpython.lltype import LowLevelType, Ptr, ContainerType
 from pypy.rpython.lltype import FuncType, functionptr, typeOf
@@ -11,6 +11,7 @@
 from pypy.rpython.rmodel import Repr, inputconst, TyperError, getfunctionptr
 from pypy.rpython.normalizecalls import perform_normalizations
 from pypy.rpython.annlowlevel import annotate_lowlevel_helper
+from pypy.rpython.exceptiondata import ExceptionData
 
 
 debug = False
@@ -36,6 +37,9 @@
             r = self.getrepr(s_primitive)
             self.primitive_to_repr[r.lowleveltype] = r
 
+    def getexceptiondata(self):
+        return self.exceptiondata    # built at the end of specialize()
+
     def getrepr(self, s_obj):
         # s_objs are not hashable... try hard to find a unique key anyway
         key = s_obj.__class__, s_obj.rtyper_makekey()
@@ -69,17 +73,25 @@
         # new blocks can be created as a result of specialize_block(), so
         # we need to be careful about the loop here.
         already_seen = {}
-        pending = self.annotator.annotated.keys()
-        while pending:
-            # specialize all blocks in the 'pending' list
-            for block in pending:
-                self.specialize_block(block)
-                already_seen[block] = True
-            # make sure all reprs so far have had their setup() called
-            self.call_all_setups()
-            # look for newly created blocks
-            pending = [block for block in self.annotator.annotated
-                             if block not in already_seen]
+
+        def specialize_more_blocks():
+            while True:
+                # look for blocks not specialized yet
+                pending = [block for block in self.annotator.annotated
+                                 if block not in already_seen]
+                if not pending:
+                    break
+                # specialize all blocks in the 'pending' list
+                for block in pending:
+                    self.specialize_block(block)
+                    already_seen[block] = True
+                # make sure all reprs so far have had their setup() called
+                self.call_all_setups()
+
+        specialize_more_blocks()
+        self.exceptiondata = ExceptionData(self)
+        specialize_more_blocks()
+
         if self.typererror:
             exc, value, tb = self.typererror
             self.typererror = None
@@ -128,6 +140,13 @@
         # insert the needed conversions on the links
         can_insert_here = block.exitswitch is None and len(block.exits) == 1
         for link in block.exits:
+            if block.exitswitch is not None and link.exitcase is not None:
+                if isinstance(block.exitswitch, Variable):
+                    r_case = self.bindingrepr(block.exitswitch)
+                else:
+                    assert block.exitswitch == Constant(last_exception)
+                    r_case = rclass.get_type_repr(self)
+                link.llexitcase = r_case.convert_const(link.exitcase)
             for a in [link.last_exception, link.last_exc_value]:
                 if isinstance(a, Variable):
                     self.setconcretetype(a)
@@ -282,8 +301,7 @@
         self.s_result = rtyper.binding(spaceop.result)
         self.args_r   = [rtyper.getrepr(s_a) for s_a in self.args_s]
         self.r_result = rtyper.getrepr(self.s_result)
-        for r in self.args_r + [self.r_result]:
-            r.setup()
+        rtyper.call_all_setups()  # compute ForwardReferences now
 
     def inputarg(self, converted_to, arg):
         """Returns the arg'th input argument of the current operation,
@@ -402,5 +420,5 @@
 from pypy.rpython import rint, rbool, rfloat
 from pypy.rpython import rslice
 from pypy.rpython import rlist, rstr, rtuple
-from pypy.rpython import rbuiltin, rpbc
+from pypy.rpython import rclass, rbuiltin, rpbc
 from pypy.rpython import rptr

Modified: pypy/dist/pypy/rpython/test/test_exception.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_exception.py	(original)
+++ pypy/dist/pypy/rpython/test/test_exception.py	Mon Jun 13 01:33:20 2005
@@ -6,6 +6,9 @@
 class MyException(Exception):
     pass
 
+class MyStrangeException:   # no (Exception) here
+    pass
+
 
 def test_simple():
     def g():
@@ -23,3 +26,27 @@
     typer.specialize()
     #t.view()
     t.checkgraphs()
+
+
+def test_exception_data():
+    def f(n):
+        raise OverflowError()
+
+    t = Translator(f)
+    a = t.annotate([int])
+    t.specialize()
+    data = t.rtyper.getexceptiondata()
+    #t.view()
+    ovferr_inst = data.ll_pyexcclass2exc(pyobjectptr(OverflowError))
+    classdef = a.bookkeeper.getclassdef(OverflowError)
+    assert ovferr_inst.typeptr == t.rtyper.class_reprs[classdef].getvtable()
+
+    keyerr_inst = data.ll_pyexcclass2exc(pyobjectptr(KeyError))
+    classdef = a.bookkeeper.getclassdef(StandardError) # most precise class seen
+    assert keyerr_inst.typeptr == t.rtyper.class_reprs[classdef].getvtable()
+
+    myerr_inst = data.ll_pyexcclass2exc(pyobjectptr(MyException))
+    assert myerr_inst.typeptr == t.rtyper.class_reprs[None].getvtable()
+
+    strgerr_inst = data.ll_pyexcclass2exc(pyobjectptr(MyStrangeException))
+    assert strgerr_inst.typeptr == t.rtyper.class_reprs[None].getvtable()



More information about the Pypy-commit mailing list