[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