[pypy-svn] r30852 - in pypy/dist/pypy: doc rpython rpython/rctypes rpython/rctypes/test

arigo at codespeak.net arigo at codespeak.net
Tue Aug 1 17:34:20 CEST 2006


Author: arigo
Date: Tue Aug  1 17:34:17 2006
New Revision: 30852

Added:
   pypy/dist/pypy/rpython/rctypes/atype.py   (contents, props changed)
   pypy/dist/pypy/rpython/rctypes/rtype.py   (contents, props changed)
Modified:
   pypy/dist/pypy/doc/rctypes.txt
   pypy/dist/pypy/rpython/extregistry.py
   pypy/dist/pypy/rpython/rctypes/aarray.py
   pypy/dist/pypy/rpython/rctypes/astringbuf.py
   pypy/dist/pypy/rpython/rctypes/implementation.py
   pypy/dist/pypy/rpython/rctypes/rarray.py
   pypy/dist/pypy/rpython/rctypes/rmodel.py
   pypy/dist/pypy/rpython/rctypes/test/test_rarray.py
Log:
Support for 'ctype*int' at run-time, to build arrays of a length which is not
a constant during annotation.


Modified: pypy/dist/pypy/doc/rctypes.txt
==============================================================================
--- pypy/dist/pypy/doc/rctypes.txt	(original)
+++ pypy/dist/pypy/doc/rctypes.txt	Tue Aug  1 17:34:17 2006
@@ -68,10 +68,11 @@
 rule that types should not be manipulated at run-time.  You can only
 call it with a constant type, as in ``POINTER(c_int)``.
 
-The create_string_buffer() function allows you to build variable-size
-arrays of chars, but there is no way to build other variable-sized
-arrays: an expression like ``c_int * n`` can only appear at
-bootstrapping-time.  (This restriction should be lifted at some point.)
+*New:* an expression like ``c_int * n`` can appear at run-time.  It can
+be used to create variable-sized array instances, i.e. arrays whose
+length is not a static constant, as in ``my_array = (c_int * n)()``.
+Similarly, the create_string_buffer() function returns a variable-size
+array of chars.
 
 NOTE: in order to translate an RPython program using ctypes, the module
 ``pypy.rpython.rctypes.implementation`` must be imported!  This is

Modified: pypy/dist/pypy/rpython/extregistry.py
==============================================================================
--- pypy/dist/pypy/rpython/extregistry.py	(original)
+++ pypy/dist/pypy/rpython/extregistry.py	Tue Aug  1 17:34:17 2006
@@ -19,16 +19,28 @@
             del selfcls._metatype_
 
     def _register_value(selfcls, key):
-        assert key not in EXT_REGISTRY_BY_VALUE
-        EXT_REGISTRY_BY_VALUE[key] = selfcls
+        if isinstance(key, (tuple, list)):
+            for k in key:
+                selfcls._register_value(k)
+        else:
+            assert key not in EXT_REGISTRY_BY_VALUE
+            EXT_REGISTRY_BY_VALUE[key] = selfcls
 
     def _register_type(selfcls, key):
-        assert key not in EXT_REGISTRY_BY_TYPE
-        EXT_REGISTRY_BY_TYPE[key] = selfcls
+        if isinstance(key, (tuple, list)):
+            for k in key:
+                selfcls._register_type(k)
+        else:
+            assert key not in EXT_REGISTRY_BY_TYPE
+            EXT_REGISTRY_BY_TYPE[key] = selfcls
 
     def _register_metatype(selfcls, key):
-        assert key not in EXT_REGISTRY_BY_METATYPE
-        EXT_REGISTRY_BY_METATYPE[key] = selfcls
+        if isinstance(key, (tuple, list)):
+            for k in key:
+                selfcls._register_metatype(k)
+        else:
+            assert key not in EXT_REGISTRY_BY_METATYPE
+            EXT_REGISTRY_BY_METATYPE[key] = selfcls
 
 
 class ExtRegistryEntry(object):

Modified: pypy/dist/pypy/rpython/rctypes/aarray.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/aarray.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/aarray.py	Tue Aug  1 17:34:17 2006
@@ -5,6 +5,28 @@
 
 ArrayType = type(ARRAY(c_int, 10))
 
+class VarSizedArrayType(object):
+    """Placeholder for ctypes array types whose size is not an
+    annotation-time constant.
+    """
+    def __init__(self, itemtype):
+        self._type_ = itemtype
+        #self._length_ = unspecified
+        self.__name__ = itemtype.__name__ + '_Array'
+
+    def get_instance_annotation(self, *args_s):
+        return SomeCTypesObject(self, ownsmemory=True)
+
+    def __eq__(self, other):
+        return (self.__class__ is other.__class__ and
+                self._type_ == other._type_)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __hash__(self):
+        return hash(self._type_)
+
 
 class CallEntry(CTypesCallEntry):
     "Annotation and rtyping of calls to array types."
@@ -19,16 +41,14 @@
         if hop.nb_args > r_array.length:
             raise TyperError("too many arguments for an array of length %d" % (
                 r_array.length,))
-        for i in range(hop.nb_args):
-            v_item = hop.inputarg(r_array.r_item, arg=i)
-            c_index = inputconst(lltype.Signed, i)
-            r_array.setitem(hop.llops, v_result, c_index, v_item)
+        items_v = hop.inputargs(*[r_array.r_item] * hop.nb_args)
+        r_array.initializeitems(hop.llops, v_result, items_v)
         return v_result
 
 
 class ObjEntry(CTypesObjEntry):
     "Annotation and rtyping of array instances."
-    _metatype_ = ArrayType
+    _metatype_ = ArrayType, VarSizedArrayType
 
     def get_field_annotation(self, s_array, fieldname):
         assert fieldname == 'value'

Modified: pypy/dist/pypy/rpython/rctypes/astringbuf.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/astringbuf.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/astringbuf.py	Tue Aug  1 17:34:17 2006
@@ -4,6 +4,12 @@
 
 from ctypes import create_string_buffer, c_char, sizeof
 
+######################################################################
+#  NOTE: astringbuf and rstringbuf should be removed and replaced    #
+#        with a regular var-sized array of char, now that we         #
+#        support var-sized arrays.                                   #
+######################################################################
+
 
 class StringBufferType(object):
     """Placeholder for the result type of create_string_buffer(),
@@ -68,7 +74,7 @@
                 return r_arg.rtype_len(hop)
         else:
             if not s_arg.is_constant():
-                raise TyperError("ctypes.sizeof(non_ctypes_object)")
+                raise TyperError("ctypes.sizeof(non_constant_type)")
             # XXX check that s_arg.const is really a ctypes type
             ctype = s_arg.const
             s_arg = SomeCTypesObject(ctype, ownsmemory=True)

Added: pypy/dist/pypy/rpython/rctypes/atype.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/rctypes/atype.py	Tue Aug  1 17:34:17 2006
@@ -0,0 +1,47 @@
+"""
+Var-sized arrays, i.e. arrays whose size is not known at annotation-time.
+"""
+
+from pypy.annotation.model import SomeCTypesObject
+from pypy.annotation.model import SomeBuiltin, SomeInteger, SomeString
+from pypy.annotation.pairtype import pair, pairtype
+from pypy.rpython.extregistry import ExtRegistryEntry
+
+
+class SomeCTypesType(SomeBuiltin):
+    """A ctypes type behaves like a built-in function, because it can only
+    be called -- with the exception of 'ctype*int' to build array types.
+    """
+    def rtyper_makerepr(self, rtyper):
+        from pypy.rpython.rctypes.rtype import TypeRepr
+        return TypeRepr(self)
+
+    def rtyper_makekey(self):
+        return SomeCTypesType, getattr(self, 'const', None)
+
+
+class SomeVarSizedCTypesType(SomeBuiltin):
+    """A ctypes built at runtime as 'ctype*int'.
+    Note that at the moment 'ctype*int*int' is not supported.
+    """
+    def __init__(self, ctype_item):
+        from pypy.rpython.rctypes.aarray import VarSizedArrayType
+        ctype_array = VarSizedArrayType(ctype_item)
+        SomeBuiltin.__init__(self, ctype_array.get_instance_annotation)
+        self.ctype_array = ctype_array
+
+    def rtyper_makerepr(self, rtyper):
+        assert self.s_self is None
+        from pypy.rpython.rctypes.rtype import VarSizedTypeRepr
+        return VarSizedTypeRepr()
+
+    def rtyper_makekey(self):
+        return SomeVarSizedCTypesType, self.ctype_array
+
+
+class __extend__(pairtype(SomeCTypesType, SomeInteger)):
+    def mul((s_ctt, s_int)):
+        entry = s_ctt.analyser.im_self   # fish fish
+        ctype_item  = entry.instance
+        return SomeVarSizedCTypesType(ctype_item)
+    mul.can_only_throw = []

Modified: pypy/dist/pypy/rpython/rctypes/implementation.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/implementation.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/implementation.py	Tue Aug  1 17:34:17 2006
@@ -92,7 +92,14 @@
 ##        annotation, e.g. callback functions."""
 
 class CTypesCallEntry(CTypesEntry):
-    "Annotation and rtyping of calls to ctypes types."
+    "Annotation and rtyping of ctypes types (mostly their calls)."
+
+    def compute_annotation(self):
+        ctype = self.instance
+        assert ctype is not None
+        analyser = self.compute_result_annotation
+        methodname = ctype.__name__
+        return SomeCTypesType(analyser, methodname=methodname)
 
     def compute_result_annotation(self, *args_s, **kwds_s):
         ctype = self.instance    # the ctype is the called object
@@ -108,6 +115,7 @@
 
 
 # Importing for side effect of registering types with extregistry
+from pypy.rpython.rctypes.atype import SomeCTypesType
 import pypy.rpython.rctypes.aprimitive
 import pypy.rpython.rctypes.apointer
 import pypy.rpython.rctypes.aarray

Modified: pypy/dist/pypy/rpython/rctypes/rarray.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/rarray.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/rarray.py	Tue Aug  1 17:34:17 2006
@@ -7,6 +7,7 @@
 from pypy.rpython.rctypes.rmodel import genreccopy_arrayitem, reccopy, C_ZERO
 from pypy.rpython.rctypes.rprimitive import PrimitiveRepr
 from pypy.rpython.rctypes.rpointer import PointerRepr
+from pypy.rpython.rctypes.aarray import VarSizedArrayType
 from pypy.annotation.model import SomeCTypesObject
 from pypy.objspace.flow.model import Constant
 
@@ -18,15 +19,22 @@
         array_ctype = s_array.knowntype
         
         item_ctype = array_ctype._type_
-        self.length = array_ctype._length_
+        if isinstance(array_ctype, VarSizedArrayType):
+            self.length = None
+        else:
+            self.length = array_ctype._length_
         
         # Find the repr and low-level type of items from their ctype
         self.r_item = rtyper.getrepr(SomeCTypesObject(item_ctype,
                                                       ownsmemory=False))
 
         # Here, self.c_data_type == self.ll_type
-        c_data_type = lltype.FixedSizeArray(self.r_item.ll_type,
-                                            self.length)
+        if self.length is not None:
+            c_data_type = lltype.FixedSizeArray(self.r_item.ll_type,
+                                                self.length)
+        else:
+            c_data_type = lltype.Array(self.r_item.ll_type,
+                                       hints={'nolength': True})
 
         super(ArrayRepr, self).__init__(rtyper, s_array, c_data_type)
 
@@ -35,8 +43,11 @@
         item_keepalive_type = self.r_item.get_content_keepalive_type()
         if not item_keepalive_type:
             return None
-        else:
+        elif self.length is not None:
             return lltype.FixedSizeArray(item_keepalive_type, self.length)
+        else:
+            raise NotImplementedError("XXX not supported yet: "
+                                      "var-sized arrays of pointers")
 
     def initialize_const(self, p, value):
         for i in range(self.length):
@@ -100,6 +111,11 @@
             genreccopy_arrayitem(llops, v_newkeepalive,
                                  v_keepalive_array, v_index)
 
+    def initializeitems(self, llops, v_array, items_v):
+        for i, v_item in enumerate(items_v):
+            c_index = inputconst(lltype.Signed, i)
+            self.setitem(llops, v_array, c_index, v_item)
+
 
 class __extend__(pairtype(ArrayRepr, IntegerRepr)):
     def rtype_getitem((r_array, r_int), hop):

Modified: pypy/dist/pypy/rpython/rctypes/rmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/rmodel.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/rmodel.py	Tue Aug  1 17:34:17 2006
@@ -115,9 +115,20 @@
                                self.r_memoryowner.lowleveltype)
 
     def allocate_instance(self, llops):
-        c1 = inputconst(lltype.Void, self.lowleveltype.TO) 
+        TYPE = self.lowleveltype.TO
+        if TYPE._is_varsize():
+            raise TyperError("allocating array with unknown length")
+        c1 = inputconst(lltype.Void, TYPE)
         return llops.genop("malloc", [c1], resulttype=self.lowleveltype)
 
+    def allocate_instance_varsize(self, llops, v_length):
+        TYPE = self.lowleveltype.TO
+        if not TYPE._is_varsize():
+            raise TyperError("allocating non-array with a specified length")
+        c1 = inputconst(lltype.Void, TYPE)
+        return llops.genop("malloc_varsize", [c1, v_length],
+                           resulttype=self.lowleveltype)
+
     def allocate_instance_ref(self, llops, v_c_data, v_c_data_owner=None):
         """Only if self.ownsmemory is false.  This allocates a new instance
         and initialize its c_data pointer."""

Added: pypy/dist/pypy/rpython/rctypes/rtype.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/rctypes/rtype.py	Tue Aug  1 17:34:17 2006
@@ -0,0 +1,42 @@
+from pypy.objspace.flow.model import Constant
+from pypy.annotation.pairtype import pairtype
+from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.rmodel import Repr, IntegerRepr, inputconst
+from pypy.rpython.error import TyperError
+from pypy.rpython.rbuiltin import BuiltinFunctionRepr
+
+
+class TypeRepr(BuiltinFunctionRepr):
+
+    def __init__(self, s_ctype):
+        assert s_ctype.s_self is None
+        if not s_ctype.is_constant():
+            raise TyperError("non-constant ctypes type object")
+        ctype = s_ctype.const
+        BuiltinFunctionRepr.__init__(self, ctype)
+
+
+class __extend__(pairtype(TypeRepr, IntegerRepr)):
+
+    def rtype_mul((r_ctype, r_int), hop):
+        v_ctype, v_repeatcount = hop.inputargs(r_ctype, lltype.Signed)
+        assert isinstance(v_ctype, Constant)
+        return v_repeatcount
+
+
+class VarSizedTypeRepr(Repr):
+    """Repr of the var-sized array type built at runtime as 'ctype*int'.
+    The ctype must be a real constant ctype, so the var-sized type can
+    be represented as just the runtime length.
+    """
+    lowleveltype = lltype.Signed
+
+    def rtype_simple_call(self, hop):
+        r_array = hop.r_result
+        args_r = [self] + [r_array.r_item] * (hop.nb_args-1)
+        args_v = hop.inputargs(*args_r)
+        v_repeatcount = args_v[0]
+        hop.exception_cannot_occur()
+        v_result = r_array.allocate_instance_varsize(hop.llops, v_repeatcount)
+        r_array.initializeitems(hop.llops, v_result, args_v[1:])
+        return v_result

Modified: pypy/dist/pypy/rpython/rctypes/test/test_rarray.py
==============================================================================
--- pypy/dist/pypy/rpython/rctypes/test/test_rarray.py	(original)
+++ pypy/dist/pypy/rpython/rctypes/test/test_rarray.py	Tue Aug  1 17:34:17 2006
@@ -163,6 +163,18 @@
             a.translator.view()
         assert s == annmodel.SomeString()
 
+    def test_annotate_varsize_array(self):
+        def func(n):
+            a = (c_int * n)()
+            a[n//2] = 12
+            return a[n//3]
+        t = TranslationContext()
+        a = t.buildannotator()
+        s = a.build_types(func, [int])
+        if conftest.option.view:
+            a.translator.view()
+        assert s.knowntype == int
+
 class Test_specialization:
     def test_specialize_array(self):
         def create_array():
@@ -282,6 +294,27 @@
         assert res.c_data[3] == 0
         assert res.c_data[4] == 0
 
+    def test_specialize_varsize_array_constructor(self):
+        def func(n):
+            return (c_int * n)()
+        res = interpret(func, [12])
+        py.test.raises(TypeError, "len(res.c_data)")    # nolength hint
+        assert res.c_data[0] == 0
+        assert res.c_data[11] == 0
+        py.test.raises(IndexError, "res.c_data[12]")
+
+    def test_specialize_varsize_array(self):
+        def func(n):
+            a = (c_int * n)(5)
+            for i in range(1, n):
+                a[i] = i
+            res = 0
+            for i in range(n):
+                res += a[i]
+            return res
+        res = interpret(func, [10])
+        assert res == 50
+
 class Test_compilation:
     def setup_class(self):
         if not test_c_compile:



More information about the Pypy-commit mailing list