[pypy-svn] r28717 - in pypy/dist/pypy: objspace/cpy objspace/cpy/test rpython rpython/lltypesystem rpython/lltypesystem/test

arigo at codespeak.net arigo at codespeak.net
Mon Jun 12 18:19:01 CEST 2006


Author: arigo
Date: Mon Jun 12 18:18:59 2006
New Revision: 28717

Modified:
   pypy/dist/pypy/objspace/cpy/objspace.py
   pypy/dist/pypy/objspace/cpy/test/test_typedef.py
   pypy/dist/pypy/objspace/cpy/typedef.py
   pypy/dist/pypy/rpython/lltypesystem/rclass.py
   pypy/dist/pypy/rpython/lltypesystem/test/test_rcpyclass.py
   pypy/dist/pypy/rpython/rcpy.py
   pypy/dist/pypy/rpython/rmodel.py
Log:
(cfbolz, arigo)
Added rcpy.cpy_typeobject() to directly get at the Python type object
corresponding to an RPython class.  Used it to finish the basic black-box
exporting of RPython objects to CPython in the cpy objspace.


Modified: pypy/dist/pypy/objspace/cpy/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/objspace.py	(original)
+++ pypy/dist/pypy/objspace/cpy/objspace.py	Mon Jun 12 18:18:59 2006
@@ -55,13 +55,18 @@
         return w_obj.value
 
     def interpclass_w(self, w_obj):
-        try:
-            w_obj, obj, follow = self.wrap_cache[id(w_obj)]
-        except KeyError:
-            from pypy.objspace.cpy.typedef import cpython2rpython
-            return cpython2rpython(self, w_obj)
-        else:
-            return obj
+        raise NotImplementedError("interpclass_w()")
+
+    def interp_w(self, RequiredClass, w_obj, can_be_None=False):
+        """
+	 Unwrap w_obj, checking that it is an instance of the required internal
+	 interpreter class (a subclass of Wrappable).
+	"""
+	if can_be_None and self.is_w(w_obj, self.w_None):
+	    return None
+        from pypy.objspace.cpy.typedef import cpython2rpython
+        return cpython2rpython(self, RequiredClass, w_obj)
+    interp_w._annspecialcase_ = 'specialize:arg(1)'
 
     # __________ operations with a direct CPython equivalent __________
 

Modified: pypy/dist/pypy/objspace/cpy/test/test_typedef.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/test/test_typedef.py	(original)
+++ pypy/dist/pypy/objspace/cpy/test/test_typedef.py	Mon Jun 12 18:18:59 2006
@@ -1,9 +1,11 @@
+import py
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.baseobjspace import Wrappable
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app, ObjSpace, W_Root
 from pypy.interpreter.function import BuiltinFunction
 from pypy.objspace.cpy.ann_policy import CPyAnnotatorPolicy
-from pypy.objspace.cpy.objspace import CPyObjSpace
+from pypy.objspace.cpy.objspace import CPyObjSpace, W_Object
 from pypy.translator.c.test.test_genc import compile
 
 
@@ -19,12 +21,12 @@
     y = W_MyType(space)
     w_x = space.wrap(x)
     w_y = space.wrap(y)
-    assert space.interpclass_w(w_x) is x
-    assert space.interpclass_w(w_y) is y
+    assert space.interp_w(W_MyType, w_x) is x
+    assert space.interp_w(W_MyType, w_y) is y
+    py.test.raises(OperationError, "space.interp_w(W_MyType, space.wrap(42))")
 
 
-def test_simple():
-    import py; py.test.skip("in-progress")
+def test_get_blackbox():
     W_MyType.typedef = TypeDef("MyType")
     space = CPyObjSpace()
 
@@ -33,5 +35,45 @@
     fn = compile(make_mytype, [],
                  annotatorpolicy = CPyAnnotatorPolicy(space))
 
-    res = fn()
+    res = fn(expected_extra_mallocs=1)
     assert type(res).__name__ == 'MyType'
+
+
+def test_blackbox():
+    W_MyType.typedef = TypeDef("MyType")
+    space = CPyObjSpace()
+
+    def mytest(w_myobj):
+        myobj = space.interp_w(W_MyType, w_myobj, can_be_None=True)
+        if myobj is None:
+            myobj = W_MyType(space)
+            myobj.abc = 1
+        myobj.abc *= 2
+        w_myobj = space.wrap(myobj)
+        w_abc = space.wrap(myobj.abc)
+        return space.newtuple([w_myobj, w_abc])
+
+    def fn(obj):
+        w_obj = W_Object(obj)
+        w_res = mytest(w_obj)
+        return w_res.value
+    fn.allow_someobjects = True
+
+    fn = compile(fn, [object],
+                 annotatorpolicy = CPyAnnotatorPolicy(space))
+
+    res, abc = fn(None, expected_extra_mallocs=1)
+    assert abc == 2
+    assert type(res).__name__ == 'MyType'
+
+    res2, abc = fn(res, expected_extra_mallocs=1)
+    assert abc == 4
+    assert res2 is res
+
+    res2, abc = fn(res, expected_extra_mallocs=1)
+    assert abc == 8
+    assert res2 is res
+
+    res2, abc = fn(res, expected_extra_mallocs=1)
+    assert abc == 16
+    assert res2 is res

Modified: pypy/dist/pypy/objspace/cpy/typedef.py
==============================================================================
--- pypy/dist/pypy/objspace/cpy/typedef.py	(original)
+++ pypy/dist/pypy/objspace/cpy/typedef.py	Mon Jun 12 18:18:59 2006
@@ -4,7 +4,11 @@
 """
 
 from pypy.objspace.cpy.capi import *
+from pypy.interpreter.error import OperationError
 from pypy.interpreter.baseobjspace import Wrappable, SpaceCache
+from pypy.rpython.objectmodel import we_are_translated
+from pypy.rpython.rcpy import CPyTypeInterface, cpy_export, cpy_import
+from pypy.rpython.rcpy import cpy_typeobject
 
 
 class rpython_object(object):
@@ -20,29 +24,70 @@
     return rpython_data.__get__(w_object.value)
 
 def rpython2cpython(space, x):
-    w_x = x.__cpy_wrapper__
-    if w_x is None:
-        w_type = space.fromcache(TypeDefCache).getorbuild(x.typedef)
-        w_x = space.call_function(w_type)
-        init_rpython_data(w_x, x)
-    return w_x
-
-def cpython2rpython(space, w_obj):
-    if isinstance(w_obj.value, rpython_object):
-        return get_rpython_data(w_obj)
+    cache = space.fromcache(TypeDefCache)
+    typeintf = cache.getorbuild(x.typedef)
+    if we_are_translated():
+        obj = cpy_export(typeintf, x)
+        return W_Object(obj)
     else:
-        return None
+        w_x = x.__cpy_wrapper__
+        if w_x is None:
+            w_type = cache.wraptypeintf(x.typedef, typeintf)
+            w_x = space.call_function(w_type)
+            init_rpython_data(w_x, x)
+        return w_x
+rpython2cpython.allow_someobjects = True
+
+def cpython2rpython(space, RequiredClass, w_obj):
+    if we_are_translated():
+        cache = space.fromcache(TypeDefCache)
+        typeintf = cache.getorbuild(RequiredClass.typedef)
+        cpytype = cpy_typeobject(typeintf, RequiredClass)
+        w_cpytype = W_Object(cpytype)
+        if space.is_true(space.isinstance(w_obj, w_cpytype)):
+            x = w_obj.value
+            return cpy_import(RequiredClass, x)
+    else:
+        try:
+            w_obj, result, follow = space.wrap_cache[id(w_obj)]
+        except KeyError:
+            if isinstance(w_obj.value, rpython_object):
+                result = get_rpython_data(w_obj)
+            else:
+                result = None
+        if isinstance(result, RequiredClass):
+            return result
+    w_objtype = space.type(w_obj)
+    w_name = space.getattr(w_objtype, space.wrap('__name__'))
+    typename = space.str_w(w_name)
+    msg = "'%s' object expected, got '%s' instead" % (
+        RequiredClass.typedef.name, typename)
+    raise OperationError(space.w_TypeError, space.wrap(msg))
+cpython2rpython._annspecialcase_ = 'specialize:arg(1)'
+cpython2rpython.allow_someobjects = True
 
 # ____________________________________________________________
 
 class TypeDefCache(SpaceCache):
+    def __init__(self, space):
+        super(TypeDefCache, self).__init__(space)
+        self.wrappedtypes = {}
+
     def build(cache, typedef):
-        space = cache.space
-        newtype = type(typedef.name, (rpython_object,), {})
-        w_result = W_Object(newtype)
-        space.wrap_cache[id(w_result)] = w_result, typedef, follow_annotations
-        return w_result
+        typeintf = CPyTypeInterface(typedef.name)
+        return typeintf
 
+    def wraptypeintf(self, typedef, typeintf):
+        # only when running on top of CPython, not for translation
+        try:
+            return self.wrappedtypes[typeintf]
+        except KeyError:
+            space = self.space
+            newtype = typeintf.emulate(rpython_object)
+            w_result = W_Object(newtype)
+            space.wrap_cache[id(w_result)] = w_result, typedef, follow_annotations
+            self.wrappedtypes[typeintf] = w_result
+            return w_result
 
 def follow_annotations(bookkeeper, w_type):
     pass

Modified: pypy/dist/pypy/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/rclass.py	Mon Jun 12 18:18:59 2006
@@ -493,13 +493,8 @@
                 vlist.insert(0, inputconst(Void, flavor))
                 if flavor == 'cpy':
                     if v_cpytype is None:
-                        cache = self.rtyper.classdef_to_pytypeobject
-                        try:
-                            cpytype = cache[self.classdef]
-                        except KeyError:
-                            from pypy.rpython import rcpy
-                            cpytype = rcpy.build_pytypeobject(self)
-                            cache[self.classdef] = cpytype
+                        from pypy.rpython import rcpy
+                        cpytype = rcpy.build_pytypeobject(self)
                         v_cpytype = inputconst(Ptr(PyObject), cpytype)
                     vlist.append(v_cpytype)
         vptr = llops.genop(mallocop, vlist,

Modified: pypy/dist/pypy/rpython/lltypesystem/test/test_rcpyclass.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/test/test_rcpyclass.py	(original)
+++ pypy/dist/pypy/rpython/lltypesystem/test/test_rcpyclass.py	Mon Jun 12 18:18:59 2006
@@ -1,5 +1,6 @@
 from pypy.translator.c.test.test_genc import compile
 from pypy.rpython.rcpy import cpy_export, cpy_import, CPyTypeInterface
+from pypy.rpython.rcpy import cpy_typeobject
 from pypy.rpython.lltypesystem import lltype
 
 
@@ -168,3 +169,13 @@
     assert type(obj).hi == 123
     assert obj.there == "foo"
     assert type(obj).there == "foo"
+
+
+def test_cpy_typeobject():
+    def f():
+        return cpy_typeobject(mytest, W_MyTest)
+
+    fn = compile(f, [])
+    typeobj = fn()
+    assert isinstance(typeobj, type)
+    assert typeobj.__name__ == 'mytest'

Modified: pypy/dist/pypy/rpython/rcpy.py
==============================================================================
--- pypy/dist/pypy/rpython/rcpy.py	(original)
+++ pypy/dist/pypy/rpython/rcpy.py	Mon Jun 12 18:18:59 2006
@@ -8,18 +8,28 @@
 
 class CPyTypeInterface(object):
 
-    def __init__(self, name, objects):
+    def __init__(self, name, objects={}):
 
         # the exported name of the type
         self.name = name
 
-        # a dict {name, pyobjectptr()} for general class attributes
+        # a dict {name: pyobjectptr()} for general class attributes
         # (not for special methods!)
-        self.objects = objects
+        self.objects = objects.copy()
 
     def _freeze_(self):
         return True
 
+    def emulate(self, rootbase):
+        "Build a type object that emulates 'self'."
+        d = {}
+        for name, value in self.objects.items():
+            assert lltype.typeOf(value) == PyObjPtr
+            assert isinstance(value._obj, lltype._pyobject)
+            d[name] = value._obj.value
+        t = type(self.name, (rootbase,), d)
+        return t
+
 
 def cpy_export(cpytype, obj):
     raise NotImplementedError("only works in translated versions")
@@ -27,6 +37,9 @@
 def cpy_import(rpytype, obj):
     raise NotImplementedError("only works in translated versions")
 
+def cpy_typeobject(cpytype, cls):
+    raise NotImplementedError("only works in translated versions")
+
 
 # ____________________________________________________________
 # Implementation
@@ -40,10 +53,7 @@
         assert isinstance(s_obj, SomeInstance)
         assert s_cpytype.is_constant()
         cpytype = s_cpytype.const
-        if hasattr(s_obj.classdef, '_cpy_exported_type_'):
-            assert s_obj.classdef._cpy_exported_type_ == cpytype
-        else:
-            s_obj.classdef._cpy_exported_type_ = cpytype
+        attach_cpy_flavor(s_obj.classdef, cpytype)
         return SomeObject()
 
     def specialize_call(self, hop):
@@ -81,6 +91,40 @@
                          resulttype = r_inst.lowleveltype)
 
 
+class Entry(ExtRegistryEntry):
+    _about_ = cpy_typeobject
+
+    def compute_result_annotation(self, s_cpytype, s_cls):
+        from pypy.annotation.model import SomeObject
+        assert s_cls.is_constant()
+        assert s_cpytype.is_constant()
+        cpytype = s_cpytype.const
+        [classdesc] = s_cls.descriptions
+        classdef = classdesc.getuniqueclassdef()
+        attach_cpy_flavor(classdef, cpytype)
+        return SomeObject()
+
+    def specialize_call(self, hop):
+        from pypy.rpython.rclass import getinstancerepr
+        s_cls = hop.args_s[1]
+        assert s_cls.is_constant()
+        [classdesc] = s_cls.descriptions
+        classdef = classdesc.getuniqueclassdef()
+        r_inst = getinstancerepr(hop.rtyper, classdef)
+        cpytype = build_pytypeobject(r_inst)
+        return hop.inputconst(PyObjPtr, cpytype)
+
+
+def attach_cpy_flavor(classdef, cpytype):
+    for parentdef in classdef.getmro():
+        if not hasattr(parentdef, '_cpy_exported_type_'):
+            parentdef._cpy_exported_type_ = None
+    if classdef._cpy_exported_type_ is None:
+        classdef._cpy_exported_type_ = cpytype
+    else:
+        assert classdef._cpy_exported_type_ == cpytype
+
+
 PyObjPtr = lltype.Ptr(lltype.PyObject)
 
 PY_TYPE_OBJECT = lltype.PyForwardReference()
@@ -148,57 +192,69 @@
     llop.gc_deallocate(lltype.Void, CPYOBJECT, addr)
 
 def build_pytypeobject(r_inst):
-    from pypy.rpython.lltypesystem.rclass import CPYOBJECTPTR
-    from pypy.rpython.rtyper import LowLevelOpList
     rtyper = r_inst.rtyper
-    typetype = lltype.pyobjectptr(type)
+    cache = rtyper.classdef_to_pytypeobject
+    try:
+        return cache[r_inst.classdef]
+    except KeyError:
+        for parentdef in r_inst.classdef.getmro():
+            cpytype = parentdef._cpy_exported_type_
+            if cpytype is not None:
+                break
+        else:
+            # for classes that cannot be exported at all
+            return lltype.nullptr(lltype.PyObject)
 
-    # make the graph of tp_new manually    
-    v1 = Variable('tp');   v1.concretetype = lltype.Ptr(PY_TYPE_OBJECT)
-    v2 = Variable('args'); v2.concretetype = PyObjPtr
-    v3 = Variable('kwds'); v3.concretetype = PyObjPtr
-    block = Block([v1, v2, v3])
-    llops = LowLevelOpList(None)
-    v4 = r_inst.new_instance(llops, v_cpytype = v1)
-    v5 = llops.genop('cast_pointer', [v4], resulttype = PyObjPtr)
-    block.operations = list(llops)
-    tp_new_graph = FunctionGraph('ll_tp_new', block)
-    block.closeblock(Link([v5], tp_new_graph.returnblock))
-    tp_new_graph.getreturnvar().concretetype = v5.concretetype
-
-    # build the PyTypeObject structure
-    pytypeobj = lltype.malloc(PY_TYPE_OBJECT, flavor='cpy',
-                              extra_args=(typetype,))
-    cpytype = r_inst.classdef._cpy_exported_type_
-    name = cpytype.name
-    T = lltype.FixedSizeArray(lltype.Char, len(name)+1)
-    p = lltype.malloc(T, immortal=True)
-    for i in range(len(name)):
-        p[i] = name[i]
-    p[len(name)] = '\x00'
-    pytypeobj.c_tp_name = lltype.direct_arrayitems(p)
-    pytypeobj.c_tp_basicsize = llmemory.sizeof(r_inst.lowleveltype.TO)
-    pytypeobj.c_tp_flags = CDefinedIntSymbolic('Py_TPFLAGS_DEFAULT')
-    pytypeobj.c_tp_new = rtyper.type_system.getcallable(tp_new_graph)
-    pytypeobj.c_tp_dealloc = rtyper.annotate_helper_fn(ll_tp_dealloc,
-                                                       [PyObjPtr])
-    result =  lltype.cast_pointer(PyObjPtr, pytypeobj)
-
-    # the llsetup function that will store the 'objects' into the
-    # type's tp_dict
-    if cpytype.objects:
-        objects = [(lltype.pyobjectptr(name), value)
-                   for name, value in cpytype.objects.items()]
-        
-        def ll_type_setup(p):
-            tp = lltype.cast_pointer(lltype.Ptr(PY_TYPE_OBJECT), p)
-            tp_dict = tp.c_tp_dict
-            for name, value in objects:
-                llop.setitem(PyObjPtr, tp_dict, name, value)
-        result._obj.setup_fnptr = rtyper.annotate_helper_fn(ll_type_setup,
-                                                            [PyObjPtr])
+        from pypy.rpython.lltypesystem.rclass import CPYOBJECTPTR
+        from pypy.rpython.rtyper import LowLevelOpList
+        typetype = lltype.pyobjectptr(type)
+
+        # make the graph of tp_new manually    
+        v1 = Variable('tp');   v1.concretetype = lltype.Ptr(PY_TYPE_OBJECT)
+        v2 = Variable('args'); v2.concretetype = PyObjPtr
+        v3 = Variable('kwds'); v3.concretetype = PyObjPtr
+        block = Block([v1, v2, v3])
+        llops = LowLevelOpList(None)
+        v4 = r_inst.new_instance(llops, v_cpytype = v1)
+        v5 = llops.genop('cast_pointer', [v4], resulttype = PyObjPtr)
+        block.operations = list(llops)
+        tp_new_graph = FunctionGraph('ll_tp_new', block)
+        block.closeblock(Link([v5], tp_new_graph.returnblock))
+        tp_new_graph.getreturnvar().concretetype = v5.concretetype
+
+        # build the PyTypeObject structure
+        pytypeobj = lltype.malloc(PY_TYPE_OBJECT, flavor='cpy',
+                                  extra_args=(typetype,))
+        name = cpytype.name
+        T = lltype.FixedSizeArray(lltype.Char, len(name)+1)
+        p = lltype.malloc(T, immortal=True)
+        for i in range(len(name)):
+            p[i] = name[i]
+        p[len(name)] = '\x00'
+        pytypeobj.c_tp_name = lltype.direct_arrayitems(p)
+        pytypeobj.c_tp_basicsize = llmemory.sizeof(r_inst.lowleveltype.TO)
+        pytypeobj.c_tp_flags = CDefinedIntSymbolic('Py_TPFLAGS_DEFAULT')
+        pytypeobj.c_tp_new = rtyper.type_system.getcallable(tp_new_graph)
+        pytypeobj.c_tp_dealloc = rtyper.annotate_helper_fn(ll_tp_dealloc,
+                                                           [PyObjPtr])
+        result =  lltype.cast_pointer(PyObjPtr, pytypeobj)
+
+        # the llsetup function that will store the 'objects' into the
+        # type's tp_dict
+        if cpytype.objects:
+            objects = [(lltype.pyobjectptr(name), value)
+                       for name, value in cpytype.objects.items()]
+
+            def ll_type_setup(p):
+                tp = lltype.cast_pointer(lltype.Ptr(PY_TYPE_OBJECT), p)
+                tp_dict = tp.c_tp_dict
+                for name, value in objects:
+                    llop.setitem(PyObjPtr, tp_dict, name, value)
+            result._obj.setup_fnptr = rtyper.annotate_helper_fn(ll_type_setup,
+                                                                [PyObjPtr])
 
-    return result
+        cache[r_inst.classdef] = result
+        return result
 
 # To make this a Py_TPFLAGS_BASETYPE, we need to have a tp_new that does
 # something different for subclasses: it needs to allocate a bit more

Modified: pypy/dist/pypy/rpython/rmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/rmodel.py	(original)
+++ pypy/dist/pypy/rpython/rmodel.py	Mon Jun 12 18:18:59 2006
@@ -422,13 +422,13 @@
 PyObjPtr = Ptr(PyObject)
 
 def getgcflavor(classdef):
-    if hasattr(classdef, '_cpy_exported_type_'):
-        return 'cpy'
-    else:
-        classdesc = classdef.classdesc
-        alloc_flavor = classdesc.read_attribute('_alloc_flavor_',
-                                                Constant('gc')).value     
-        return alloc_flavor
+    for parentdef in classdef.getmro():
+        if hasattr(parentdef, '_cpy_exported_type_'):
+            return 'cpy'
+    classdesc = classdef.classdesc
+    alloc_flavor = classdesc.read_attribute('_alloc_flavor_',
+                                            Constant('gc')).value
+    return alloc_flavor
 
 def externalvsinternal(rtyper, item_repr): # -> external_item_repr, (internal_)item_repr
     from pypy.rpython import rclass



More information about the Pypy-commit mailing list