[pypy-commit] pypy jit-targets: hg merge default

hakanardo noreply at buildbot.pypy.org
Sun Dec 11 15:20:01 CET 2011


Author: Hakan Ardo <hakan at debian.org>
Branch: jit-targets
Changeset: r50385:988827261d3c
Date: 2011-12-11 14:25 +0100
http://bitbucket.org/pypy/pypy/changeset/988827261d3c/

Log:	hg merge default

diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -252,6 +252,10 @@
                    "use small tuples",
                    default=False),
 
+        BoolOption("withspecialisedtuple",
+                   "use specialised tuples",
+                   default=False),
+
         BoolOption("withrope", "use ropes as the string implementation",
                    default=False,
                    requires=[("objspace.std.withstrslice", False),
@@ -365,6 +369,7 @@
         config.objspace.std.suggest(optimized_list_getitem=True)
         config.objspace.std.suggest(getattributeshortcut=True)
         config.objspace.std.suggest(newshortcut=True)
+        config.objspace.std.suggest(withspecialisedtuple=True)
         #if not IS_64_BITS:
         #    config.objspace.std.suggest(withsmalllong=True)
 
diff --git a/pypy/conftest.py b/pypy/conftest.py
--- a/pypy/conftest.py
+++ b/pypy/conftest.py
@@ -496,6 +496,17 @@
     def setup(self):
         super(AppClassCollector, self).setup()
         cls = self.obj
+        #
+        # <hack>
+        for name in dir(cls):
+            if name.startswith('test_'):
+                func = getattr(cls, name, None)
+                code = getattr(func, 'func_code', None)
+                if code and code.co_flags & 32:
+                    raise AssertionError("unsupported: %r is a generator "
+                                         "app-level test method" % (name,))
+        # </hack>
+        #
         space = cls.space
         clsname = cls.__name__
         if self.config.option.runappdirect:
diff --git a/pypy/jit/tl/pypyjit_demo.py b/pypy/jit/tl/pypyjit_demo.py
--- a/pypy/jit/tl/pypyjit_demo.py
+++ b/pypy/jit/tl/pypyjit_demo.py
@@ -2,13 +2,15 @@
 pypyjit.set_param(threshold=200)
 
 
+def g(*args):
+    return len(args)
+
 def f(n):
-    pairs = [(0.0, 1.0), (2.0, 3.0)] * n
-    mag = 0
-    for (x1, x2) in pairs:
-        dx = x1 - x2
-        mag += ((dx * dx ) ** (-1.5))            
-    return n
+    s = 0
+    for i in range(n):
+        l = [i, n, 2]
+        s += g(*l)
+    return s
 
 try:
     print f(301)
diff --git a/pypy/module/_hashlib/interp_hashlib.py b/pypy/module/_hashlib/interp_hashlib.py
--- a/pypy/module/_hashlib/interp_hashlib.py
+++ b/pypy/module/_hashlib/interp_hashlib.py
@@ -21,11 +21,11 @@
 
 class W_Hash(Wrappable):
     ctx = lltype.nullptr(ropenssl.EVP_MD_CTX.TO)
-    _block_size = -1
 
     def __init__(self, space, name):
         self.name = name
-        self.digest_size = self.compute_digest_size()
+        digest_type = self.digest_type_by_name(space)
+        self.digest_size = rffi.getintfield(digest_type, 'c_md_size')
 
         # Allocate a lock for each HASH object.
         # An optimization would be to not release the GIL on small requests,
@@ -34,21 +34,22 @@
 
         ctx = lltype.malloc(ropenssl.EVP_MD_CTX.TO, flavor='raw')
         rgc.add_memory_pressure(HASH_MALLOC_SIZE + self.digest_size)
+        ropenssl.EVP_DigestInit(ctx, digest_type)
         self.ctx = ctx
 
-    def initdigest(self, space, name):
-        digest = ropenssl.EVP_get_digestbyname(name)
-        if not digest:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("unknown hash function"))
-        ropenssl.EVP_DigestInit(self.ctx, digest)
-
     def __del__(self):
         # self.lock.free()
         if self.ctx:
             ropenssl.EVP_MD_CTX_cleanup(self.ctx)
             lltype.free(self.ctx, flavor='raw')
 
+    def digest_type_by_name(self, space):
+        digest_type = ropenssl.EVP_get_digestbyname(self.name)
+        if not digest_type:
+            raise OperationError(space.w_ValueError,
+                                 space.wrap("unknown hash function"))
+        return digest_type
+
     def descr_repr(self, space):
         addrstring = self.getaddrstring(space)
         return space.wrap("<%s HASH object at 0x%s>" % (
@@ -87,7 +88,9 @@
         return space.wrap(self.digest_size)
 
     def get_block_size(self, space):
-        return space.wrap(self.compute_block_size())
+        digest_type = self.digest_type_by_name(space)
+        block_size = rffi.getintfield(digest_type, 'c_block_size')
+        return space.wrap(block_size)
 
     def _digest(self, space):
         with lltype.scoped_alloc(ropenssl.EVP_MD_CTX.TO) as ctx:
@@ -99,36 +102,6 @@
                 ropenssl.EVP_MD_CTX_cleanup(ctx)
                 return rffi.charpsize2str(digest, digest_size)
 
-    def compute_digest_size(self):
-        # XXX This isn't the nicest way, but the EVP_MD_size OpenSSL
-        # XXX function is defined as a C macro on OS X and would be
-        # XXX significantly harder to implement in another way.
-        # Values are digest sizes in bytes
-        return {
-            'md5':    16, 'MD5':    16,
-            'sha1':   20, 'SHA1':   20,
-            'sha224': 28, 'SHA224': 28,
-            'sha256': 32, 'SHA256': 32,
-            'sha384': 48, 'SHA384': 48,
-            'sha512': 64, 'SHA512': 64,
-            }.get(self.name, 0)
-
-    def compute_block_size(self):
-        if self._block_size != -1:
-            return self._block_size
-        # XXX This isn't the nicest way, but the EVP_MD_CTX_block_size
-        # XXX OpenSSL function is defined as a C macro on some systems
-        # XXX and would be significantly harder to implement in
-        # XXX another way.
-        self._block_size = {
-            'md5':     64, 'MD5':     64,
-            'sha1':    64, 'SHA1':    64,
-            'sha224':  64, 'SHA224':  64,
-            'sha256':  64, 'SHA256':  64,
-            'sha384': 128, 'SHA384': 128,
-            'sha512': 128, 'SHA512': 128,
-            }.get(self.name, 0)
-        return self._block_size
 
 W_Hash.typedef = TypeDef(
     'HASH',
@@ -142,11 +115,11 @@
     digestsize=GetSetProperty(W_Hash.get_digest_size),
     block_size=GetSetProperty(W_Hash.get_block_size),
     )
+W_Hash.acceptable_as_base_class = False
 
 @unwrap_spec(name=str, string='bufferstr')
 def new(space, name, string=''):
     w_hash = W_Hash(space, name)
-    w_hash.initdigest(space, name)
     w_hash.update(space, string)
     return space.wrap(w_hash)
 
@@ -158,6 +131,6 @@
         return new(space, name, string)
     return new_hash
 
-for name in algorithms:
-    newname = 'new_%s' % (name,)
-    globals()[newname] = make_new_hash(name, newname)
+for _name in algorithms:
+    _newname = 'new_%s' % (_name,)
+    globals()[_newname] = make_new_hash(_name, _newname)
diff --git a/pypy/module/_hashlib/test/test_hashlib.py b/pypy/module/_hashlib/test/test_hashlib.py
--- a/pypy/module/_hashlib/test/test_hashlib.py
+++ b/pypy/module/_hashlib/test/test_hashlib.py
@@ -80,28 +80,27 @@
         _hashlib.openssl_sha1(b).digest()
 
     def test_extra_algorithms(self):
-        import _hashlib
-        test_string = "Nobody inspects the spammish repetition"
         expected_results = {
             "md5": "bb649c83dd1ea5c9d9dec9a18df0ffe9",
             "md4": "c275b8454684ea416b93d7a418b43176",
             "mdc2": None,   # XXX find the correct expected value
             "sha": "e2b0a8609b47c58e5d984c9ccfe69f9b654b032b",
             "ripemd160": "cc4a5ce1b3df48aec5d22d1f16b894a0b894eccc",
-            "whirlpool": "1a22b79fe5afda02c63a25927193ed01dc718b74"
-                         "026e597608ce431f9c3d2c9e74a7350b7fbb7c5d"
-                         "4effe5d7a31879b8b7a10fd2f544c4ca268ecc6793923583",
+            "whirlpool": ("1a22b79fe5afda02c63a25927193ed01dc718b74"
+                          "026e597608ce431f9c3d2c9e74a7350b7fbb7c5d"
+                          "4effe5d7a31879b8b7a10fd2f544c4ca268ecc6793923583"),
             }
-        def extracheck(hash_name, expected):
+        import _hashlib
+        test_string = "Nobody inspects the spammish repetition"
+        for hash_name, expected in sorted(expected_results.items()):
             try:
                 m = _hashlib.new(hash_name)
             except ValueError, e:
-                skip('%s: %s' % (hash_name, e))
+                print 'skipped %s: %s' % (hash_name, e)
+                continue
             m.update(test_string)
             got = m.hexdigest()
             assert got and type(got) is str and len(got) % 2 == 0
             got.decode('hex')
             if expected is not None:
                 assert got == expected
-        for hash_name, expected in sorted(expected_results.items()):
-            yield extracheck, hash_name, expected
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -14,7 +14,6 @@
     METH_VARARGS, build_type_checkers, PyObjectFields, bootstrap_function)
 from pypy.module.cpyext.pyerrors import PyErr_Occurred
 from pypy.rlib.objectmodel import we_are_translated
-from pypy.objspace.std.tupleobject import W_TupleObject
 
 PyCFunction_typedef = rffi.COpaquePtr(typedef='PyCFunction')
 PyCFunction = lltype.Ptr(lltype.FuncType([PyObject, PyObject], PyObject))
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -42,11 +42,11 @@
     which case o is returned.  Use PySequence_Fast_GET_ITEM() to access the
     members of the result.  Returns NULL on failure.  If the object is not a
     sequence, raises TypeError with m as the message text."""
-    if (space.is_true(space.isinstance(w_obj, space.w_list)) or
-        space.is_true(space.isinstance(w_obj, space.w_tuple))):
+    if (isinstance(w_obj, listobject.W_ListObject) or
+        isinstance(w_obj, tupleobject.W_TupleObject)):
         return w_obj
     try:
-        return space.newtuple(space.fixedview(w_obj))
+        return tupleobject.W_TupleObject(space.fixedview(w_obj))
     except OperationError:
         raise OperationError(space.w_TypeError, space.wrap(rffi.charp2str(m)))
 
diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -6,13 +6,12 @@
     borrow_from, make_ref, from_ref)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject
-from pypy.objspace.std.smalltupleobject import W_SmallTupleObject
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
 
 @cpython_api([Py_ssize_t], PyObject)
 def PyTuple_New(space, size):
-    return space.newtuple([space.w_None] * size)
+    return W_TupleObject([space.w_None] * size)
 
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
 def PyTuple_SetItem(space, w_t, pos, w_obj):
@@ -24,12 +23,12 @@
     return 0
 
 def _setitem_tuple(w_t, pos, w_obj):
-    if isinstance(w_t, W_TupleObject):
-        w_t.wrappeditems[pos] = w_obj
-    elif isinstance(w_t, W_SmallTupleObject):
-        w_t.setitem(pos, w_obj)
-    else:
-        assert False
+    # this function checks that w_t is really a W_TupleObject.  It
+    # should only ever be called with a freshly built tuple from
+    # PyTuple_New(), which always return a W_TupleObject, even if there
+    # are also other implementations of tuples.
+    assert isinstance(w_t, W_TupleObject)
+    w_t.wrappeditems[pos] = w_obj
 
 @cpython_api([PyObject, Py_ssize_t], PyObject)
 def PyTuple_GetItem(space, w_t, pos):
diff --git a/pypy/module/micronumpy/interp_boxes.py b/pypy/module/micronumpy/interp_boxes.py
--- a/pypy/module/micronumpy/interp_boxes.py
+++ b/pypy/module/micronumpy/interp_boxes.py
@@ -91,9 +91,6 @@
     descr_neg = _unaryop_impl("negative")
     descr_abs = _unaryop_impl("absolute")
 
-    def descr_tolist(self, space):
-        return self.get_dtype(space).itemtype.to_builtin_type(space, self)
-
 
 class W_BoolBox(W_GenericBox, PrimitiveBox):
     descr__new__, get_dtype = new_dtype_getter("bool")
@@ -182,8 +179,6 @@
 
     __neg__ = interp2app(W_GenericBox.descr_neg),
     __abs__ = interp2app(W_GenericBox.descr_abs),
-
-    tolist = interp2app(W_GenericBox.descr_tolist),
 )
 
 W_BoolBox.typedef = TypeDef("bool_", W_GenericBox.typedef,
diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -876,17 +876,6 @@
             arr.setshape(space, new_shape)
         return arr
 
-    def descr_tolist(self, space):
-        if len(self.shape) == 0:
-            assert isinstance(self, Scalar)
-            return self.value.descr_tolist(space)
-        w_result = space.newlist([])
-        for i in range(self.shape[0]):
-            space.call_method(w_result, "append",
-                space.call_method(self.descr_getitem(space, space.wrap(i)), "tolist")
-            )
-        return w_result
-
     def descr_mean(self, space):
         return space.div(self.descr_sum(space), space.wrap(self.find_size()))
 
@@ -1496,7 +1485,6 @@
 
     copy = interp2app(BaseArray.descr_copy),
     reshape = interp2app(BaseArray.descr_reshape),
-    tolist = interp2app(BaseArray.descr_tolist),
 )
 
 
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -879,45 +879,6 @@
         b[0] = 3
         assert b.__debug_repr__() == 'Call2(add, forced=Array)'
 
-    def test_tolist_scalar(self):
-        from numpypy import int32, bool_
-        x = int32(23)
-        assert x.tolist() == 23
-        assert type(x.tolist()) is int
-        y = bool_(True)
-        assert y.tolist() is True
-
-    def test_tolist_zerodim(self):
-        from numpypy import array
-        x = array(3)
-        assert x.tolist() == 3
-        assert type(x.tolist()) is int
-
-    def test_tolist_singledim(self):
-        from numpypy import array
-        a = array(range(5))
-        assert a.tolist() == [0, 1, 2, 3, 4]
-        assert type(a.tolist()[0]) is int
-        b = array([0.2, 0.4, 0.6])
-        assert b.tolist() == [0.2, 0.4, 0.6]
-
-    def test_tolist_multidim(self):
-        from numpypy import array
-        a = array([[1, 2], [3, 4]])
-        assert a.tolist() == [[1, 2], [3, 4]]
-
-    def test_tolist_view(self):
-        from numpypy import array
-        a = array([[1,2],[3,4]])
-        assert (a + a).tolist() == [[2, 4], [6, 8]]
-
-    def test_tolist_slice(self):
-        from numpypy import array
-        a = array([[17.1, 27.2], [40.3, 50.3]])
-        assert a[:,0].tolist() == [17.1, 40.3]
-        assert a[0].tolist() == [17.1, 27.2]
-
-
 class AppTestMultiDim(BaseNumpyAppTest):
     def test_init(self):
         import numpypy
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -78,9 +78,6 @@
         w_obj.__init__(self._coerce(space, w_item).value)
         return w_obj
 
-    def to_builtin_type(self, space, box):
-        return space.wrap(self.unbox(box))
-
     def _coerce(self, space, w_item):
         raise NotImplementedError
 
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -694,14 +694,16 @@
         return self.wrap(r)
 
     @jit.look_inside_iff(lambda self, w_list:
-            jit.isconstant(w_list.length()) and w_list.length() < UNROLL_CUTOFF)
+           jit.isconstant(w_list.length()) and w_list.length() < UNROLL_CUTOFF)
     def getitems_copy(self, w_list):
         return [self.wrap(item) for item in self.unerase(w_list.lstorage)]
 
     @jit.unroll_safe
     def getitems_unroll(self, w_list):
         return [self.wrap(item) for item in self.unerase(w_list.lstorage)]
-    @jit.dont_look_inside
+
+    @jit.look_inside_iff(lambda self, w_list:
+           jit.isconstant(w_list.length()) and w_list.length() < UNROLL_CUTOFF)
     def getitems_fixedsize(self, w_list):
         return self.getitems_unroll(w_list)
 
diff --git a/pypy/objspace/std/model.py b/pypy/objspace/std/model.py
--- a/pypy/objspace/std/model.py
+++ b/pypy/objspace/std/model.py
@@ -15,6 +15,7 @@
     _registered_implementations.add(implcls)
 
 option_to_typename = {
+    "withspecialisedtuple" : ["specialisedtupleobject.W_SpecialisedTupleObject"],
     "withsmalltuple" : ["smalltupleobject.W_SmallTupleObject"],
     "withsmallint"   : ["smallintobject.W_SmallIntObject"],
     "withsmalllong"  : ["smalllongobject.W_SmallLongObject"],
@@ -261,6 +262,11 @@
             self.typeorder[smalltupleobject.W_SmallTupleObject] += [
                 (tupleobject.W_TupleObject, smalltupleobject.delegate_SmallTuple2Tuple)]
 
+        if config.objspace.std.withspecialisedtuple:
+            from pypy.objspace.std import specialisedtupleobject
+            self.typeorder[specialisedtupleobject.W_SpecialisedTupleObject] += [
+                (tupleobject.W_TupleObject, specialisedtupleobject.delegate_SpecialisedTuple2Tuple)]
+
         # put W_Root everywhere
         self.typeorder[W_Root] = []
         for type in self.typeorder:
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -29,7 +29,7 @@
 from pypy.objspace.std.sliceobject import W_SliceObject
 from pypy.objspace.std.smallintobject import W_SmallIntObject
 from pypy.objspace.std.stringobject import W_StringObject
-from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.tupleobject import W_AbstractTupleObject
 from pypy.objspace.std.typeobject import W_TypeObject
 
 # types
@@ -391,8 +391,8 @@
                 self.wrap("expected length %d, got %d" % (expected, got)))
 
     def unpackiterable(self, w_obj, expected_length=-1):
-        if isinstance(w_obj, W_TupleObject):
-            t = w_obj.wrappeditems[:]
+        if isinstance(w_obj, W_AbstractTupleObject):
+            t = w_obj.getitems_copy()
         elif isinstance(w_obj, W_ListObject):
             t = w_obj.getitems_copy()
         else:
@@ -405,8 +405,8 @@
     def fixedview(self, w_obj, expected_length=-1, unroll=False):
         """ Fast paths
         """
-        if isinstance(w_obj, W_TupleObject):
-            t = w_obj.wrappeditems
+        if isinstance(w_obj, W_AbstractTupleObject):
+            t = w_obj.tolist()
         elif isinstance(w_obj, W_ListObject):
             if unroll:
                 t = w_obj.getitems_unroll()
@@ -430,8 +430,8 @@
     def listview(self, w_obj, expected_length=-1):
         if isinstance(w_obj, W_ListObject):
             t = w_obj.getitems()
-        elif isinstance(w_obj, W_TupleObject):
-            t = w_obj.wrappeditems[:]
+        elif isinstance(w_obj, W_AbstractTupleObject):
+            t = w_obj.getitems_copy()
         else:
             return ObjSpace.unpackiterable(self, w_obj, expected_length)
         if expected_length != -1 and len(t) != expected_length:
diff --git a/pypy/objspace/std/smalltupleobject.py b/pypy/objspace/std/smalltupleobject.py
--- a/pypy/objspace/std/smalltupleobject.py
+++ b/pypy/objspace/std/smalltupleobject.py
@@ -9,13 +9,14 @@
 from pypy.interpreter import gateway
 from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib.unroll import unrolling_iterable
+from pypy.tool.sourcetools import func_with_new_name
 from pypy.objspace.std.tupleobject import W_AbstractTupleObject, W_TupleObject
 
 class W_SmallTupleObject(W_AbstractTupleObject):
     from pypy.objspace.std.tupletype import tuple_typedef as typedef
 
-    def tolist(self):
-        raise NotImplementedError
+    #def tolist(self):   --- inherited from W_AbstractTupleObject
+    #    raise NotImplementedError
 
     def length(self):
         raise NotImplementedError
@@ -51,6 +52,9 @@
                 l[i] = getattr(self, 'w_value%s' % i)
             return l
 
+        # same source code, but builds and returns a resizable list
+        getitems_copy = func_with_new_name(tolist, 'getitems_copy')
+
         def length(self):
             return n
 
diff --git a/pypy/objspace/std/specialisedtupleobject.py b/pypy/objspace/std/specialisedtupleobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/specialisedtupleobject.py
@@ -0,0 +1,302 @@
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.model import registerimplementation
+from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.objspace.std.tupleobject import W_AbstractTupleObject
+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
+from pypy.rlib.rarithmetic import intmask
+from pypy.rlib.objectmodel import compute_hash
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.tool.sourcetools import func_with_new_name
+
+class NotSpecialised(Exception):
+    pass
+
+class W_SpecialisedTupleObject(W_AbstractTupleObject):
+    from pypy.objspace.std.tupletype import tuple_typedef as typedef
+    __slots__ = []
+
+    def __repr__(self):
+        """ representation for debugging purposes """
+        reprlist = [repr(item) for item in self._to_unwrapped_list()]
+        return "%s(%s)" % (self.__class__.__name__, ', '.join(reprlist))
+
+    #def tolist(self):   --- inherited from W_AbstractTupleObject
+    #    raise NotImplementedError
+
+    def _to_unwrapped_list(self):
+        "NOT_RPYTHON"
+        raise NotImplementedError
+
+    def length(self):
+        raise NotImplementedError
+
+    def getitem(self, index):
+        raise NotImplementedError
+
+    def hash(self, space):
+        raise NotImplementedError
+
+    def eq(self, space, w_other):
+        raise NotImplementedError
+
+    def setitem(self, index, w_item):
+        raise NotImplementedError
+
+    def unwrap(self, space):
+        return tuple(self._to_unwrapped_list())
+
+    def delegating(self):
+        pass     # for tests only
+
+
+def make_specialised_class(typetuple):
+    assert type(typetuple) == tuple
+    
+    nValues = len(typetuple)
+    iter_n = unrolling_iterable(range(nValues))
+    
+    class cls(W_SpecialisedTupleObject):
+        def __init__(self, space, *values_w):
+            self.space = space
+            assert len(values_w) == nValues
+            for i in iter_n:
+                w_obj = values_w[i]
+                val_type = typetuple[i]
+                if val_type == int:
+                    unwrapped = space.int_w(w_obj)
+                elif val_type == float:
+                    unwrapped = space.float_w(w_obj)
+                elif val_type == str:
+                    unwrapped = space.str_w(w_obj)
+                elif val_type == object:
+                    unwrapped = w_obj
+                else:
+                    raise AssertionError
+                setattr(self, 'value%s' % i, unwrapped)
+
+        def length(self):
+            return nValues
+
+        def tolist(self):
+            list_w = [None] * nValues            
+            for i in iter_n:
+                value = getattr(self, 'value%s' % i)
+                if typetuple[i] != object:
+                    value = self.space.wrap(value)
+                list_w[i] = value
+            return list_w
+
+        # same source code, but builds and returns a resizable list
+        getitems_copy = func_with_new_name(tolist, 'getitems_copy')
+
+        def _to_unwrapped_list(self):
+            "NOT_RPYTHON"
+            list_w = [None] * nValues
+            for i in iter_n:
+                value = getattr(self, 'value%s' % i)
+                if typetuple[i] == object:
+                    value = self.space.unwrap(value)
+                list_w[i] = value
+            return list_w
+
+        def hash(self, space):
+            # XXX duplicate logic from tupleobject.py
+            mult = 1000003
+            x = 0x345678
+            z = nValues
+            for i in iter_n:
+                value = getattr(self, 'value%s' % i)
+                if typetuple[i] == object:
+                    y = space.int_w(space.hash(value))
+                elif typetuple[i] == float:
+                    # get the correct hash for float which is an
+                    # integer & other less frequent cases
+                    from pypy.objspace.std.floatobject import _hash_float
+                    y = _hash_float(space, value)
+                else:
+                    y = compute_hash(value)
+                x = (x ^ y) * mult
+                z -= 1
+                mult += 82520 + z + z
+            x += 97531
+            return space.wrap(intmask(x))
+
+        def _eq(self, w_other):
+            if not isinstance(w_other, cls):
+                # if we are not comparing same types, give up
+                raise FailedToImplement
+            for i in iter_n:
+                myval    = getattr(self,    'value%s' % i)
+                otherval = getattr(w_other, 'value%s' % i)
+                if typetuple[i] == object:
+                    if not self.space.eq_w(myval, otherval):
+                        return False
+                else:
+                    if myval != otherval:
+                        return False
+            else:
+                return True
+
+        def eq(self, space, w_other):
+            return space.newbool(self._eq(w_other))
+
+        def ne(self, space, w_other):
+            return space.newbool(not self._eq(w_other))
+
+##        def _compare(self, compare_op, w_other):
+##            if not isinstance(w_other, cls):
+##                raise FailedToImplement
+##            ncmp = min(self.length(), w_other.length())
+##            for i in iter_n:
+##                if typetuple[i] == Any:#like space.eq on wrapped or two params?
+##                    raise FailedToImplement
+##                if ncmp > i:
+##                    l_val = getattr(self, 'value%s' % i)
+##                    r_val = getattr(w_other, 'value%s' % i)
+##                    if l_val != r_val:
+##                        return compare_op(l_val, r_val)
+##            return compare_op(self.length(), w_other.length())
+
+        def getitem(self, index):
+            for i in iter_n:
+                if index == i:
+                    value = getattr(self, 'value%s' % i)
+                    if typetuple[i] != object:
+                        value = self.space.wrap(value)
+                    return value
+            raise IndexError
+
+    cls.__name__ = ('W_SpecialisedTupleObject_' +
+                    ''.join([t.__name__[0] for t in typetuple]))
+    _specialisations.append(cls)
+    return cls
+
+# ---------- current specialized versions ----------
+
+_specialisations = []
+Cls_ii = make_specialised_class((int, int))
+Cls_is = make_specialised_class((int, str))
+Cls_io = make_specialised_class((int, object))
+Cls_si = make_specialised_class((str, int))
+Cls_ss = make_specialised_class((str, str))
+Cls_so = make_specialised_class((str, object))
+Cls_oi = make_specialised_class((object, int))
+Cls_os = make_specialised_class((object, str))
+Cls_oo = make_specialised_class((object, object))
+Cls_ff = make_specialised_class((float, float))
+Cls_ooo = make_specialised_class((object, object, object))
+
+def makespecialisedtuple(space, list_w):
+    if len(list_w) == 2:
+        w_arg1, w_arg2 = list_w
+        w_type1 = space.type(w_arg1)
+        w_type2 = space.type(w_arg2)
+        #
+        if w_type1 is space.w_int:
+            if w_type2 is space.w_int:
+                return Cls_ii(space, w_arg1, w_arg2)
+            elif w_type2 is space.w_str:
+                return Cls_is(space, w_arg1, w_arg2)
+            else:
+                return Cls_io(space, w_arg1, w_arg2)
+        #
+        elif w_type1 is space.w_str:
+            if w_type2 is space.w_int:
+                return Cls_si(space, w_arg1, w_arg2)
+            elif w_type2 is space.w_str:
+                return Cls_ss(space, w_arg1, w_arg2)
+            else:
+                return Cls_so(space, w_arg1, w_arg2)
+        #
+        elif w_type1 is space.w_float and w_type2 is space.w_float:
+            return Cls_ff(space, w_arg1, w_arg2)
+        #
+        else:
+            if w_type2 is space.w_int:
+                return Cls_oi(space, w_arg1, w_arg2)
+            elif w_type2 is space.w_str:
+                return Cls_os(space, w_arg1, w_arg2)
+            else:
+                return Cls_oo(space, w_arg1, w_arg2)
+        #
+    elif len(list_w) == 3:
+        return Cls_ooo(space, list_w[0], list_w[1], list_w[2])
+    else:
+        raise NotSpecialised
+
+# ____________________________________________________________
+
+registerimplementation(W_SpecialisedTupleObject)
+
+def delegate_SpecialisedTuple2Tuple(space, w_specialised):
+    w_specialised.delegating()
+    return W_TupleObject(w_specialised.tolist())
+
+def len__SpecialisedTuple(space, w_tuple):
+    return space.wrap(w_tuple.length())
+
+def getitem__SpecialisedTuple_ANY(space, w_tuple, w_index):
+    index = space.getindex_w(w_index, space.w_IndexError, "tuple index")
+    if index < 0:
+        index += w_tuple.length()
+    try:
+        return w_tuple.getitem(index)
+    except IndexError:
+        raise OperationError(space.w_IndexError,
+                             space.wrap("tuple index out of range"))
+
+def getitem__SpecialisedTuple_Slice(space, w_tuple, w_slice):
+    length = w_tuple.length()
+    start, stop, step, slicelength = w_slice.indices4(space, length)
+    assert slicelength >= 0
+    subitems = [None] * slicelength
+    for i in range(slicelength):
+        subitems[i] = w_tuple.getitem(start)
+        start += step
+    return space.newtuple(subitems)
+
+def mul_specialisedtuple_times(space, w_tuple, w_times):
+    try:
+        times = space.getindex_w(w_times, space.w_OverflowError)
+    except OperationError, e:
+        if e.match(space, space.w_TypeError):
+            raise FailedToImplement
+        raise
+    if times == 1 and space.type(w_tuple) == space.w_tuple:
+        return w_tuple
+    items = w_tuple.tolist()
+    return space.newtuple(items * times)
+
+def mul__SpecialisedTuple_ANY(space, w_tuple, w_times):
+    return mul_specialisedtuple_times(space, w_tuple, w_times)
+
+def mul__ANY_SpecialisedTuple(space, w_times, w_tuple):
+    return mul_specialisedtuple_times(space, w_tuple, w_times)
+
+def eq__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+    return w_tuple1.eq(space, w_tuple2)
+
+def ne__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+    return w_tuple1.ne(space, w_tuple2)
+
+##from operator import lt, le, ge, gt   
+ 
+##def lt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+##    return space.newbool(w_tuple1._compare(lt, w_tuple2))
+
+##def le__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+##    return space.newbool(w_tuple1._compare(le, w_tuple2))
+
+##def ge__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+##    return space.newbool(w_tuple1._compare(ge, w_tuple2))
+
+##def gt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+##    return space.newbool(w_tuple1._compare(gt, w_tuple2))
+
+def hash__SpecialisedTuple(space, w_tuple):
+    return w_tuple.hash(space)
+
+from pypy.objspace.std import tupletype
+register_all(vars(), tupletype)
diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py b/pypy/objspace/std/test/test_specialisedtupleobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_specialisedtupleobject.py
@@ -0,0 +1,234 @@
+import py, sys
+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.specialisedtupleobject import W_SpecialisedTupleObject
+from pypy.objspace.std.specialisedtupleobject import _specialisations
+from pypy.interpreter.error import OperationError
+from pypy.conftest import gettestobjspace, option
+from pypy.objspace.std.test import test_tupleobject
+from pypy.interpreter import gateway
+
+
+for cls in _specialisations:
+    globals()[cls.__name__] = cls
+
+
+class TestW_SpecialisedTupleObject():
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withspecialisedtuple": True})
+
+    def test_isspecialisedtupleobjectintint(self):
+        w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+        assert isinstance(w_tuple, W_SpecialisedTupleObject_ii)
+        
+    def test_isnotspecialisedtupleobject(self):
+        w_tuple = self.space.newtuple([self.space.wrap({})])
+        assert not isinstance(w_tuple, W_SpecialisedTupleObject)
+        
+    def test_specialisedtupleclassname(self):
+        w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+        assert w_tuple.__class__.__name__ == 'W_SpecialisedTupleObject_ii'
+
+    def test_hash_against_normal_tuple(self):
+        N_space = gettestobjspace(**{"objspace.std.withspecialisedtuple": False})
+        S_space = gettestobjspace(**{"objspace.std.withspecialisedtuple": True})
+        
+        def hash_test(values):
+            N_values_w = [N_space.wrap(value) for value in values]
+            S_values_w = [S_space.wrap(value) for value in values]
+            N_w_tuple = N_space.newtuple(N_values_w)
+            S_w_tuple = S_space.newtuple(S_values_w)
+    
+            assert isinstance(S_w_tuple, W_SpecialisedTupleObject)
+            assert isinstance(N_w_tuple, W_TupleObject)
+            assert not N_space.is_true(N_space.eq(N_w_tuple, S_w_tuple))
+            assert S_space.is_true(S_space.eq(N_w_tuple, S_w_tuple))
+            assert S_space.is_true(S_space.eq(N_space.hash(N_w_tuple), S_space.hash(S_w_tuple)))
+
+        hash_test([1,2])
+        hash_test([1.5,2.8])
+        hash_test([1.0,2.0])
+        hash_test(['arbitrary','strings'])
+        hash_test([1,(1,2,3,4)])
+        hash_test([1,(1,2)])
+        hash_test([1,('a',2)])
+        hash_test([1,()])
+        hash_test([1,2,3])
+
+
+class AppTestW_SpecialisedTupleObject:
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withspecialisedtuple": True})
+        def forbid_delegation(space, w_tuple):
+            def delegation_forbidden():
+                # haaaack
+                co = sys._getframe(2).f_code
+                if co.co_name.startswith('_mm_repr_tuple'):
+                    return
+                raise OperationError(space.w_ReferenceError, w_tuple)
+            w_tuple.delegating = delegation_forbidden
+            return w_tuple
+        if option.runappdirect:
+            cls.w_forbid_delegation = lambda self, x: x
+            cls.test_delegation = lambda self: skip("runappdirect")
+        else:
+            cls.w_forbid_delegation = cls.space.wrap(
+                gateway.interp2app(forbid_delegation))
+
+    def w_isspecialised(self, obj, expected=''):
+        import __pypy__
+        r = __pypy__.internal_repr(obj)
+        print obj, '==>', r, '   (expected: %r)' % expected
+        return ("SpecialisedTupleObject" + expected) in r
+
+    def test_createspecialisedtuple(self):
+        spec = {int: 'i',
+                float: 'f',
+                str: 's',
+                list: 'o'}
+        #
+        for x in [42, 4.2, "foo", []]:
+            for y in [43, 4.3, "bar", []]:
+                expected1 = spec[type(x)]
+                expected2 = spec[type(y)]
+                if (expected1 == 'f') ^ (expected2 == 'f'):
+                    if expected1 == 'f': expected1 = 'o'
+                    if expected2 == 'f': expected2 = 'o'
+                obj = (x, y)
+                assert self.isspecialised(obj, '_' + expected1 + expected2)
+        #
+        obj = (1, 2, 3)
+        assert self.isspecialised(obj, '_ooo')
+
+    def test_delegation(self):
+        t = self.forbid_delegation((42, 43))
+        raises(ReferenceError, t.__getslice__, 0, 1)
+
+    def test_len(self):
+        t = self.forbid_delegation((42,43))
+        assert len(t) == 2
+
+    def test_notspecialisedtuple(self):
+        assert not self.isspecialised((42,43,44,45))
+        assert not self.isspecialised((1.5,))
+
+    def test_slicing_to_specialised(self):
+        t = (1, 2, 3)
+        assert self.isspecialised(t[0:2])
+        t = (1, '2', 3)
+        assert self.isspecialised(t[0:5:2])
+
+    def test_adding_to_specialised(self):
+        t = (1,)
+        assert self.isspecialised(t + (2,))
+
+    def test_multiply_to_specialised(self):
+        t = (1,)
+        assert self.isspecialised(t * 2)
+
+    def test_slicing_from_specialised(self):
+        t = (1, 2, 3)
+        assert t[0:2:1] == (1, 2)
+
+    def test_eq_no_delegation(self):
+        t = (1,)
+        a = self.forbid_delegation(t + (2,))
+        b = (1, 2)
+        assert a == b
+
+        c = (2, 1)
+        assert not a == c
+
+    def test_eq_can_delegate(self):        
+        a = (1,2)
+        b = (1,3,2)
+        assert not a == b
+
+        values = [2, 2L, 2.0, 1, 1L, 1.0]
+        for x in values:
+            for y in values:
+                assert ((1,2) == (x,y)) == (1 == x and 2 == y)
+
+    def test_neq(self):
+        a = self.forbid_delegation((1,2))
+        b = (1,)
+        b = b+(2,)
+        assert not a != b
+        
+        c = (1,3)
+        assert a != c
+        
+    def test_ordering(self):
+        a = (1,2) #self.forbid_delegation((1,2)) --- code commented out
+        assert a <  (2,2)    
+        assert a <  (1,3)    
+        assert not a <  (1,2) 
+
+        assert a <=  (2,2)    
+        assert a <=  (1,2) 
+        assert not a <=  (1,1) 
+           
+        assert a >= (0,2)    
+        assert a >= (1,2)    
+        assert not a >= (1,3)    
+        
+        assert a > (0,2)    
+        assert a > (1,1)    
+        assert not a > (1,3)    
+
+        assert (2,2) > a
+        assert (1,3) > a
+        assert not (1,2) > a
+           
+        assert (2,2) >= a
+        assert (1,2) >= a
+        assert not (1,1) >= a
+           
+        assert (0,2) <= a
+        assert (1,2) <= a
+        assert not (1,3) <= a
+        
+        assert (0,2) < a
+        assert (1,1) < a
+        assert not (1,3) < a
+
+    def test_hash(self):
+        a = (1,2)
+        b = (1,)
+        b += (2,) # else a and b refer to same constant
+        assert hash(a) == hash(b)
+
+        c = (2,4)
+        assert hash(a) != hash(c)
+
+        assert hash(a) == hash((1L, 2L)) == hash((1.0, 2.0)) == hash((1.0, 2L))
+
+    def test_getitem(self):
+        t = self.forbid_delegation((5,3))
+        assert (t)[0] == 5
+        assert (t)[1] == 3
+        assert (t)[-1] == 3
+        assert (t)[-2] == 5
+        raises(IndexError, "t[2]")
+        raises(IndexError, "t[-3]")
+
+    def test_three_tuples(self):
+        b = self.forbid_delegation((1, 2, 3))
+        c = (1,)
+        d = c + (2, 3)
+        assert self.isspecialised(d)
+        assert b == d
+
+    def test_mongrel(self):
+        a = self.forbid_delegation((1, 2.2, '333'))
+        assert self.isspecialised(a)
+        assert len(a) == 3
+        assert a[0] == 1 and a[1] == 2.2 and a[2] == '333'
+        b = ('333',)
+        assert a == (1, 2.2,) + b
+        assert not a != (1, 2.2) + b
+
+
+class AppTestAll(test_tupleobject.AppTestW_TupleObject):
+    pass
diff --git a/pypy/objspace/std/test/test_tupleobject.py b/pypy/objspace/std/test/test_tupleobject.py
--- a/pypy/objspace/std/test/test_tupleobject.py
+++ b/pypy/objspace/std/test/test_tupleobject.py
@@ -280,6 +280,8 @@
         assert () * 10 == ()
         assert (5,) * 3 == (5,5,5)
         assert (5,2) * 2 == (5,2,5,2)
+
+    def test_mul_identity(self):
         t = (1,2,3)
         assert (t * 1) is t
 
diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py
--- a/pypy/objspace/std/tupleobject.py
+++ b/pypy/objspace/std/tupleobject.py
@@ -12,6 +12,15 @@
 class W_AbstractTupleObject(W_Object):
     __slots__ = ()
 
+    def tolist(self):
+        "Returns the items, as a fixed-size list."
+        raise NotImplementedError
+
+    def getitems_copy(self):
+        "Returns a copy of the items, as a resizable list."
+        raise NotImplementedError
+
+
 class W_TupleObject(W_AbstractTupleObject):
     from pypy.objspace.std.tupletype import tuple_typedef as typedef
     _immutable_fields_ = ['wrappeditems[*]']
@@ -29,6 +38,12 @@
         items = [space.unwrap(w_item) for w_item in w_tuple.wrappeditems]
         return tuple(items)
 
+    def tolist(self):
+        return self.wrappeditems
+
+    def getitems_copy(self):
+        return self.wrappeditems[:]   # returns a resizable list
+
 registerimplementation(W_TupleObject)
 
 
diff --git a/pypy/objspace/std/tupletype.py b/pypy/objspace/std/tupletype.py
--- a/pypy/objspace/std/tupletype.py
+++ b/pypy/objspace/std/tupletype.py
@@ -5,6 +5,14 @@
 
 def wraptuple(space, list_w):
     from pypy.objspace.std.tupleobject import W_TupleObject
+
+    if space.config.objspace.std.withspecialisedtuple:
+        from specialisedtupleobject import makespecialisedtuple, NotSpecialised
+        try:
+            return makespecialisedtuple(space, list_w)
+        except NotSpecialised:
+            pass
+
     if space.config.objspace.std.withsmalltuple:
         from pypy.objspace.std.smalltupleobject import W_SmallTupleObject2
         from pypy.objspace.std.smalltupleobject import W_SmallTupleObject3
diff --git a/pypy/rlib/ropenssl.py b/pypy/rlib/ropenssl.py
--- a/pypy/rlib/ropenssl.py
+++ b/pypy/rlib/ropenssl.py
@@ -110,6 +110,10 @@
         'struct GENERAL_NAME_st',
         [('type', rffi.INT),
          ])
+    EVP_MD_st = rffi_platform.Struct(
+        'EVP_MD',
+        [('md_size', rffi.INT),
+         ('block_size', rffi.INT)])
     EVP_MD_SIZE = rffi_platform.SizeOf('EVP_MD')
     EVP_MD_CTX_SIZE = rffi_platform.SizeOf('EVP_MD_CTX')
 
@@ -258,7 +262,7 @@
              [BIO, rffi.VOIDP, rffi.VOIDP, rffi.VOIDP], X509)
 
 EVP_MD_CTX = rffi.COpaquePtr('EVP_MD_CTX', compilation_info=eci)
-EVP_MD     = rffi.COpaquePtr('EVP_MD', compilation_info=eci)
+EVP_MD     = lltype.Ptr(EVP_MD_st)
 
 OpenSSL_add_all_digests = external(
     'OpenSSL_add_all_digests', [], lltype.Void)
diff --git a/pypy/tool/pytest/appsupport.py b/pypy/tool/pytest/appsupport.py
--- a/pypy/tool/pytest/appsupport.py
+++ b/pypy/tool/pytest/appsupport.py
@@ -63,7 +63,10 @@
     exec_ = eval
 
     def repr(self, w_value):
-        return self.space.unwrap(self.space.repr(w_value))
+        try:
+            return self.space.unwrap(self.space.repr(w_value))
+        except Exception, e:
+            return "<Sorry, exception while trying to do repr, %r>"%e
 
     def is_true(self, w_value):
         return self.space.is_true(w_value)


More information about the pypy-commit mailing list