Author: Tim Felgentreff <timfelgentr...@gmail.com>
Branch: 
Changeset: r89901:c613943ef39a
Date: 2017-02-02 11:07 +0100
http://bitbucket.org/pypy/pypy/changeset/c613943ef39a/

Log:    merge default

diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -102,7 +102,11 @@
                 % (len(buf) + offset, size + offset))
         raw_addr = buf._pypy_raw_address()
         result = self.from_address(raw_addr)
-        result._ensure_objects()['ffffffff'] = obj
+        objects = result._ensure_objects()
+        if objects is not None:
+            objects['ffffffff'] = obj
+        else:   # case e.g. of a primitive type like c_int
+            result._objects = obj
         return result
 
     def from_buffer_copy(self, obj, offset=0):
diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -198,10 +198,21 @@
 
     def getdict(self, space):
         if self.w_func_dict is None:
+            if not self.can_change_code:
+                raise oefmt(space.w_AttributeError,
+                            "cannot set extra attributes on built-in 
functions")
             self.w_func_dict = space.newdict(instance=True)
         return self.w_func_dict
 
+    def getdictvalue(self, space, attr):
+        if not self.can_change_code:
+            return None
+        return W_Root.getdictvalue(self, space, attr)
+
     def setdict(self, space, w_dict):
+        if not self.can_change_code:
+            raise oefmt(space.w_AttributeError,
+                        "cannot set __dict__ on built-in functions")
         if not space.isinstance_w(w_dict, space.w_dict):
             raise oefmt(space.w_TypeError,
                         "setting function's dictionary to a non-dict")
@@ -660,7 +671,7 @@
         Function.__init__(self, func.space, func.code, func.w_func_globals,
                           func.defs_w, func.closure, func.name)
         self.w_doc = func.w_doc
-        self.w_func_dict = func.w_func_dict
+        #self.w_func_dict = func.w_func_dict---nowadays, always None
         self.w_module = func.w_module
 
     def descr_builtinfunction__new__(space, w_subtype):
diff --git a/pypy/interpreter/test/test_function.py 
b/pypy/interpreter/test/test_function.py
--- a/pypy/interpreter/test/test_function.py
+++ b/pypy/interpreter/test/test_function.py
@@ -95,8 +95,16 @@
     def test_write_code_builtin_forbidden(self):
         def f(*args):
             return 42
-            raises(TypeError, "dir.func_code = f.func_code")
-            raises(TypeError, "list.append.im_func.func_code = f.func_code")
+        raises(TypeError, "dir.func_code = f.func_code")
+        raises(TypeError, "list.append.im_func.func_code = f.func_code")
+
+    def test_write_extra_attributes_builtin_forbidden(self):
+        raises(AttributeError, "dir.abcd = 5")
+        raises(AttributeError, "list.append.im_func.efgh = 6")
+        raises(AttributeError, "dir.__dict__")
+        raises(AttributeError, "dir.__dict__ = {}")
+        c = all.__call__   # this should work
+        assert c([4, 5, 6]) is True
 
     def test_set_module_to_name_eagerly(self):
         skip("fails on PyPy but works on CPython.  Unsure we want to care")
diff --git a/pypy/module/array/interp_array.py 
b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -122,12 +122,120 @@
                                         hints={'nolength': True}))
 
 class W_ArrayBase(W_Root):
-    _attrs_ = ('space', 'len', 'allocated', '_lifeline_') # no buffer
+    _attrs_ = ('space', 'len', 'allocated', '_lifeline_', '_buffer')
 
     def __init__(self, space):
         self.space = space
         self.len = 0
         self.allocated = 0
+        self._buffer = lltype.nullptr(rffi.CCHARP.TO)
+
+    @rgc.must_be_light_finalizer
+    def __del__(self):
+        if self._buffer:
+            lltype.free(self._buffer, flavor='raw')
+
+    def setlen(self, size, zero=False, overallocate=True):
+        if size > 0:
+            if size > self.allocated or size < self.allocated / 2:
+                if overallocate:
+                    if size < 9:
+                        some = 3
+                    else:
+                        some = 6
+                    some += size >> 3
+                else:
+                    some = 0
+                self.allocated = size + some
+                byte_size = self.allocated * self.itemsize
+                if zero:
+                    new_buffer = lltype.malloc(
+                        rffi.CCHARP.TO, byte_size, flavor='raw',
+                        add_memory_pressure=True, zero=True)
+                else:
+                    new_buffer = lltype.malloc(
+                        rffi.CCHARP.TO, byte_size, flavor='raw',
+                        add_memory_pressure=True)
+                    copy_bytes = min(size, self.len) * self.itemsize
+                    rffi.c_memcpy(rffi.cast(rffi.VOIDP, new_buffer),
+                                  rffi.cast(rffi.VOIDP, self._buffer),
+                                  copy_bytes)
+            else:
+                self.len = size
+                return
+        else:
+            assert size == 0
+            self.allocated = 0
+            new_buffer = lltype.nullptr(rffi.CCHARP.TO)
+
+        if self._buffer:
+            lltype.free(self._buffer, flavor='raw')
+        self._buffer = new_buffer
+        self.len = size
+
+    def _fromiterable(self, w_seq):
+        # used by fromsequence().
+        # a more careful case if w_seq happens to be a very large
+        # iterable: don't copy the items into some intermediate list
+        w_iterator = self.space.iter(w_seq)
+        tp = self.space.type(w_iterator)
+        while True:
+            unpack_driver.jit_merge_point(selfclass=self.__class__,
+                                          tp=tp, self=self,
+                                          w_iterator=w_iterator)
+            space = self.space
+            try:
+                w_item = space.next(w_iterator)
+            except OperationError as e:
+                if not e.match(space, space.w_StopIteration):
+                    raise
+                break  # done
+            self.descr_append(space, w_item)
+
+    def _charbuf_start(self):
+        return self._buffer
+
+    def _buffer_as_unsigned(self):
+        return rffi.cast(lltype.Unsigned, self._buffer)
+
+    def _charbuf_stop(self):
+        keepalive_until_here(self)
+
+    def delitem(self, space, i, j):
+        if i < 0:
+            i += self.len
+        if i < 0:
+            i = 0
+        if j < 0:
+            j += self.len
+        if j < 0:
+            j = 0
+        if j > self.len:
+            j = self.len
+        if i >= j:
+            return None
+        oldbuffer = self._buffer
+        self._buffer = lltype.malloc(rffi.CCHARP.TO,
+            (self.len - (j - i)) * self.itemsize, flavor='raw',
+            add_memory_pressure=True)
+        if i:
+            rffi.c_memcpy(
+                rffi.cast(rffi.VOIDP, self._buffer),
+                rffi.cast(rffi.VOIDP, oldbuffer),
+                i * self.itemsize
+            )
+        if j < self.len:
+            rffi.c_memcpy(
+                rffi.cast(rffi.VOIDP, rffi.ptradd(self._buffer,
+                                                  i * self.itemsize)),
+                rffi.cast(rffi.VOIDP, rffi.ptradd(oldbuffer,
+                                                  j * self.itemsize)),
+                (self.len - j) * self.itemsize
+            )
+        self.len -= j - i
+        self.allocated = self.len
+        if oldbuffer:
+            lltype.free(oldbuffer, flavor='raw')
 
     def readbuf_w(self, space):
         return ArrayBuffer(self, True)
@@ -154,14 +262,24 @@
 
         Return number of occurrences of x in the array.
         """
-        raise NotImplementedError
+        cnt = 0
+        for i in range(self.len):
+            # XXX jitdriver
+            w_item = self.w_getitem(space, i)
+            if space.is_true(space.eq(w_item, w_val)):
+                cnt += 1
+        return space.wrap(cnt)
 
     def descr_index(self, space, w_x):
         """ index(x)
 
         Return index of first occurrence of x in the array.
         """
-        raise NotImplementedError
+        for i in range(self.len):
+            w_item = self.w_getitem(space, i)
+            if space.is_true(space.eq(w_item, w_x)):
+                return space.wrap(i)
+        raise oefmt(space.w_ValueError, "array.index(x): x not in list")
 
     def descr_reverse(self, space):
         """ reverse()
@@ -175,7 +293,8 @@
 
         Remove the first occurrence of x in the array.
         """
-        raise NotImplementedError
+        w_idx = self.descr_index(space, w_val)
+        self.descr_pop(space, space.int_w(w_idx))
 
     @unwrap_spec(i=int)
     def descr_pop(self, space, i=-1):
@@ -453,16 +572,102 @@
         return space.newseqiter(self)
 
     def descr_add(self, space, w_other):
-        raise NotImplementedError
+        if (not isinstance(w_other, W_ArrayBase)
+                or w_other.typecode != self.typecode):
+            return space.w_NotImplemented
+        a = self.constructor(space)
+        a.setlen(self.len + w_other.len, overallocate=False)
+        if self.len:
+            rffi.c_memcpy(
+                rffi.cast(rffi.VOIDP, a._buffer),
+                rffi.cast(rffi.VOIDP, self._buffer),
+                self.len * self.itemsize
+            )
+        if w_other.len:
+            rffi.c_memcpy(
+                rffi.cast(rffi.VOIDP, rffi.ptradd(a._buffer,
+                                             self.len * self.itemsize)),
+                rffi.cast(rffi.VOIDP, w_other._buffer),
+                w_other.len * self.itemsize
+            )
+        keepalive_until_here(self)
+        keepalive_until_here(a)
+        return a
 
     def descr_inplace_add(self, space, w_other):
-        raise NotImplementedError
+        if (not isinstance(w_other, W_ArrayBase)
+                or w_other.typecode != self.typecode):
+            return space.w_NotImplemented
+        oldlen = self.len
+        otherlen = w_other.len
+        self.setlen(oldlen + otherlen)
+        if otherlen:
+            rffi.c_memcpy(
+                rffi.cast(rffi.VOIDP, rffi.ptradd(self._buffer,
+                                             oldlen * self.itemsize)),
+                rffi.cast(rffi.VOIDP, w_other._buffer),
+                otherlen * self.itemsize
+            )
+        keepalive_until_here(self)
+        keepalive_until_here(w_other)
+        return self
+
+    def _mul_helper(self, space, w_repeat, is_inplace):
+        try:
+            repeat = space.getindex_w(w_repeat, space.w_OverflowError)
+        except OperationError as e:
+            if e.match(space, space.w_TypeError):
+                return space.w_NotImplemented
+            raise
+        if is_inplace:
+            a = self
+            start = 1
+        else:
+            a = self.constructor(space)
+            start = 0
+        if repeat <= start:
+            if repeat <= 0:
+                a.setlen(0, overallocate=False)
+            return a
+        oldlen = self.len
+        try:
+            newlen = ovfcheck(oldlen * repeat)
+        except OverflowError:
+            raise MemoryError
+        #
+        srcbuf = self._buffer
+        srcsize = self.len * self.itemsize
+        for i in range(srcsize):
+            if srcbuf[i] != '\x00':
+                break
+        else:
+            # the source is entirely zero: initialize the target
+            # with zeroes too
+            a.setlen(newlen, zero=True, overallocate=False)
+            return a
+        #
+        a.setlen(newlen, overallocate=False)
+        srcbuf = self._buffer   # reload this, in case self is a
+        if oldlen == 1:
+            self._repeat_single_item(a, start, repeat)
+        else:
+            dstbuf = a._buffer
+            if start == 1:
+                dstbuf = rffi.ptradd(dstbuf, srcsize)
+            for r in range(start, repeat):
+                rffi.c_memcpy(rffi.cast(rffi.VOIDP, dstbuf),
+                              rffi.cast(rffi.VOIDP, srcbuf),
+                              srcsize)
+                dstbuf = rffi.ptradd(dstbuf, srcsize)
+        keepalive_until_here(self)
+        keepalive_until_here(a)
+        return a
 
     def descr_mul(self, space, w_repeat):
-        raise NotImplementedError
+        return self._mul_helper(space, w_repeat, False)
 
     def descr_inplace_mul(self, space, w_repeat):
-        raise NotImplementedError
+        return self._mul_helper(space, w_repeat, True)
 
     def descr_radd(self, space, w_other):
         return self.descr_add(space, w_other)
@@ -551,6 +756,7 @@
         self.itemtype = itemtype
         self.bytes = rffi.sizeof(itemtype)
         self.arraytype = lltype.Array(itemtype, hints={'nolength': True})
+        self.arrayptrtype = lltype.Ptr(self.arraytype)
         self.unwrap = unwrap
         self.signed = signed
         self.canoverflow = canoverflow
@@ -640,22 +846,21 @@
         return self.array._charbuf_start()
 
 
+unpack_driver = jit.JitDriver(name='unpack_array',
+                              greens=['selfclass', 'tp'],
+                              reds=['self', 'w_iterator'])
+
 def make_array(mytype):
     W_ArrayBase = globals()['W_ArrayBase']
 
-    unpack_driver = jit.JitDriver(name='unpack_array',
-                                  greens=['tp'],
-                                  reds=['self', 'w_iterator'])
-
     class W_Array(W_ArrayBase):
         itemsize = mytype.bytes
         typecode = mytype.typecode
 
-        _attrs_ = ('space', 'len', 'allocated', '_lifeline_', 'buffer')
+        _attrs_ = W_ArrayBase._attrs_
 
-        def __init__(self, space):
-            W_ArrayBase.__init__(self, space)
-            self.buffer = lltype.nullptr(mytype.arraytype)
+        def get_buffer(self):
+            return rffi.cast(mytype.arrayptrtype, self._buffer)
 
         def item_w(self, w_item):
             space = self.space
@@ -709,46 +914,6 @@
                                          self.space.wrap(msg))
             return result
 
-        @rgc.must_be_light_finalizer
-        def __del__(self):
-            if self.buffer:
-                lltype.free(self.buffer, flavor='raw')
-
-        def setlen(self, size, zero=False, overallocate=True):
-            if size > 0:
-                if size > self.allocated or size < self.allocated / 2:
-                    if overallocate:
-                        if size < 9:
-                            some = 3
-                        else:
-                            some = 6
-                        some += size >> 3
-                    else:
-                        some = 0
-                    self.allocated = size + some
-                    if zero:
-                        new_buffer = lltype.malloc(
-                            mytype.arraytype, self.allocated, flavor='raw',
-                            add_memory_pressure=True, zero=True)
-                    else:
-                        new_buffer = lltype.malloc(
-                            mytype.arraytype, self.allocated, flavor='raw',
-                            add_memory_pressure=True)
-                        for i in range(min(size, self.len)):
-                            new_buffer[i] = self.buffer[i]
-                else:
-                    self.len = size
-                    return
-            else:
-                assert size == 0
-                self.allocated = 0
-                new_buffer = lltype.nullptr(mytype.arraytype)
-
-            if self.buffer:
-                lltype.free(self.buffer, flavor='raw')
-            self.buffer = new_buffer
-            self.len = size
-
         def fromsequence(self, w_seq):
             space = self.space
             oldlen = self.len
@@ -764,20 +929,21 @@
             if lst is not None:
                 self.setlen(oldlen + len(lst))
                 try:
-                    buf = self.buffer
+                    buf = self.get_buffer()
                     for num in lst:
                         buf[newlen] = self.item_from_int_or_float(num)
                         newlen += 1
                 except OperationError:
                     self.setlen(newlen)
                     raise
+                keepalive_until_here(self)
                 return
 
             # this is the common case: w_seq is a list or a tuple
             lst_w = space.listview_no_unpack(w_seq)
             if lst_w is not None:
                 self.setlen(oldlen + len(lst_w))
-                buf = self.buffer
+                buf = self.get_buffer()
                 try:
                     for w_num in lst_w:
                         # note: self.item_w() might invoke arbitrary code.
@@ -788,30 +954,14 @@
                         buf[newlen] = self.item_w(w_num)
                         newlen += 1
                 except OperationError:
-                    if buf == self.buffer:
+                    if buf == self.get_buffer():
                         self.setlen(newlen)
                     raise
+                keepalive_until_here(self)
                 return
 
             self._fromiterable(w_seq)
 
-        def _fromiterable(self, w_seq):
-            # a more careful case if w_seq happens to be a very large
-            # iterable: don't copy the items into some intermediate list
-            w_iterator = self.space.iter(w_seq)
-            tp = self.space.type(w_iterator)
-            while True:
-                unpack_driver.jit_merge_point(tp=tp, self=self,
-                                              w_iterator=w_iterator)
-                space = self.space
-                try:
-                    w_item = space.next(w_iterator)
-                except OperationError as e:
-                    if not e.match(space, space.w_StopIteration):
-                        raise
-                    break  # done
-                self.descr_append(space, w_item)
-
         def extend(self, w_iterable, accept_different_array=False):
             space = self.space
             if isinstance(w_iterable, W_Array):
@@ -819,11 +969,14 @@
                 new = w_iterable.len
                 self.setlen(self.len + new)
                 i = 0
+                buf = self.get_buffer()
+                srcbuf = w_iterable.get_buffer()
                 while i < new:
                     if oldlen + i >= self.len:
                         self.setlen(oldlen + i + 1)
-                    self.buffer[oldlen + i] = w_iterable.buffer[i]
+                    buf[oldlen + i] = srcbuf[i]
                     i += 1
+                keepalive_until_here(w_iterable)
                 self.setlen(oldlen + i)
             elif (not accept_different_array
                   and isinstance(w_iterable, W_ArrayBase)):
@@ -832,17 +985,9 @@
             else:
                 self.fromsequence(w_iterable)
 
-        def _charbuf_start(self):
-            return rffi.cast(rffi.CCHARP, self.buffer)
-
-        def _buffer_as_unsigned(self):
-            return rffi.cast(lltype.Unsigned, self.buffer)
-
-        def _charbuf_stop(self):
+        def w_getitem(self, space, idx):
+            item = self.get_buffer()[idx]
             keepalive_until_here(self)
-
-        def w_getitem(self, space, idx):
-            item = self.buffer[idx]
             if mytype.typecode in 'bBhHil':
                 item = rffi.cast(lltype.Signed, item)
             elif mytype.typecode == 'f':
@@ -855,29 +1000,16 @@
             x = self.item_w(w_x)
             index = self.len
             self.setlen(index + 1)
-            self.buffer[index] = x
+            self.get_buffer()[index] = x
+            keepalive_until_here(self)
 
         # List interface
-        def descr_count(self, space, w_val):
-            cnt = 0
-            for i in range(self.len):
-                # XXX jitdriver
-                w_item = self.w_getitem(space, i)
-                if space.is_true(space.eq(w_item, w_val)):
-                    cnt += 1
-            return space.wrap(cnt)
-
-        def descr_index(self, space, w_val):
-            for i in range(self.len):
-                w_item = self.w_getitem(space, i)
-                if space.is_true(space.eq(w_item, w_val)):
-                    return space.wrap(i)
-            raise oefmt(space.w_ValueError, "array.index(x): x not in list")
 
         def descr_reverse(self, space):
-            b = self.buffer
+            b = self.get_buffer()
             for i in range(self.len / 2):
                 b[i], b[self.len - i - 1] = b[self.len - i - 1], b[i]
+            keepalive_until_here(self)
 
         def descr_pop(self, space, i):
             if i < 0:
@@ -885,16 +1017,14 @@
             if i < 0 or i >= self.len:
                 raise oefmt(space.w_IndexError, "pop index out of range")
             w_val = self.w_getitem(space, i)
+            b = self.get_buffer()
             while i < self.len - 1:
-                self.buffer[i] = self.buffer[i + 1]
+                b[i] = b[i + 1]
                 i += 1
+            keepalive_until_here(self)
             self.setlen(self.len - 1)
             return w_val
 
-        def descr_remove(self, space, w_val):
-            w_idx = self.descr_index(space, w_val)
-            self.descr_pop(space, space.int_w(w_idx))
-
         def descr_insert(self, space, idx, w_val):
             if idx < 0:
                 idx += self.len
@@ -906,10 +1036,12 @@
             val = self.item_w(w_val)
             self.setlen(self.len + 1)
             i = self.len - 1
+            b = self.get_buffer()
             while i > idx:
-                self.buffer[i] = self.buffer[i - 1]
+                b[i] = b[i - 1]
                 i -= 1
-            self.buffer[i] = val
+            b[i] = val
+            keepalive_until_here(self)
 
         def getitem_slice(self, space, w_idx):
             start, stop, step, size = space.decode_index4(w_idx, self.len)
@@ -917,9 +1049,13 @@
             w_a.setlen(size, overallocate=False)
             assert step != 0
             j = 0
+            buf = w_a.get_buffer()
+            srcbuf = self.get_buffer()
             for i in range(start, stop, step):
-                w_a.buffer[j] = self.buffer[i]
+                buf[j] = srcbuf[i]
                 j += 1
+            keepalive_until_here(self)
+            keepalive_until_here(w_a)
             return w_a
 
         def setitem(self, space, w_idx, w_item):
@@ -928,7 +1064,8 @@
                 raise oefmt(self.space.w_TypeError,
                             "can only assign array to array slice")
             item = self.item_w(w_item)
-            self.buffer[idx] = item
+            self.get_buffer()[idx] = item
+            keepalive_until_here(self)
 
         def setitem_slice(self, space, w_idx, w_item):
             if not isinstance(w_item, W_Array):
@@ -945,127 +1082,21 @@
                 self.fromsequence(w_lst)
             else:
                 j = 0
+                buf = self.get_buffer()
+                srcbuf = w_item.get_buffer()
                 for i in range(start, stop, step):
-                    self.buffer[i] = w_item.buffer[j]
+                    buf[i] = srcbuf[j]
                     j += 1
+                keepalive_until_here(w_item)
+                keepalive_until_here(self)
 
-        def delitem(self, space, i, j):
-            if i < 0:
-                i += self.len
-            if i < 0:
-                i = 0
-            if j < 0:
-                j += self.len
-            if j < 0:
-                j = 0
-            if j > self.len:
-                j = self.len
-            if i >= j:
-                return None
-            oldbuffer = self.buffer
-            self.buffer = lltype.malloc(
-                mytype.arraytype, max(self.len - (j - i), 0), flavor='raw',
-                add_memory_pressure=True)
-            if i:
-                rffi.c_memcpy(
-                    rffi.cast(rffi.VOIDP, self.buffer),
-                    rffi.cast(rffi.VOIDP, oldbuffer),
-                    i * mytype.bytes
-                )
-            if j < self.len:
-                rffi.c_memcpy(
-                    rffi.cast(rffi.VOIDP, rffi.ptradd(self.buffer, i)),
-                    rffi.cast(rffi.VOIDP, rffi.ptradd(oldbuffer, j)),
-                    (self.len - j) * mytype.bytes
-                )
-            self.len -= j - i
-            self.allocated = self.len
-            if oldbuffer:
-                lltype.free(oldbuffer, flavor='raw')
-
-        # Add and mul methods
-        def descr_add(self, space, w_other):
-            if not isinstance(w_other, W_Array):
-                return space.w_NotImplemented
-            a = mytype.w_class(space)
-            a.setlen(self.len + w_other.len, overallocate=False)
-            if self.len:
-                rffi.c_memcpy(
-                    rffi.cast(rffi.VOIDP, a.buffer),
-                    rffi.cast(rffi.VOIDP, self.buffer),
-                    self.len * mytype.bytes
-                )
-            if w_other.len:
-                rffi.c_memcpy(
-                    rffi.cast(rffi.VOIDP, rffi.ptradd(a.buffer, self.len)),
-                    rffi.cast(rffi.VOIDP, w_other.buffer),
-                    w_other.len * mytype.bytes
-                )
-            return a
-
-        def descr_inplace_add(self, space, w_other):
-            if not isinstance(w_other, W_Array):
-                return space.w_NotImplemented
-            oldlen = self.len
-            otherlen = w_other.len
-            self.setlen(oldlen + otherlen)
-            if otherlen:
-                rffi.c_memcpy(
-                    rffi.cast(rffi.VOIDP, rffi.ptradd(self.buffer, oldlen)),
-                    rffi.cast(rffi.VOIDP, w_other.buffer),
-                    otherlen * mytype.bytes
-                )
-            return self
-
-        def descr_mul(self, space, w_repeat):
-            return _mul_helper(space, self, w_repeat, False)
-
-        def descr_inplace_mul(self, space, w_repeat):
-            return _mul_helper(space, self, w_repeat, True)
-
-    def _mul_helper(space, self, w_repeat, is_inplace):
-        try:
-            repeat = space.getindex_w(w_repeat, space.w_OverflowError)
-        except OperationError as e:
-            if e.match(space, space.w_TypeError):
-                return space.w_NotImplemented
-            raise
-        repeat = max(repeat, 0)
-        try:
-            newlen = ovfcheck(self.len * repeat)
-        except OverflowError:
-            raise MemoryError
-        oldlen = self.len
-        if is_inplace:
-            a = self
-            start = 1
-        else:
-            a = mytype.w_class(space)
-            start = 0
-        # <a performance hack>
-        if oldlen == 1:
-            if mytype.unwrap == 'str_w' or mytype.unwrap == 'unicode_w':
-                zero = not ord(self.buffer[0])
-            elif mytype.unwrap == 'int_w' or mytype.unwrap == 'bigint_w':
-                zero = not widen(self.buffer[0])
-            #elif mytype.unwrap == 'float_w':
-            #    value = ...float(self.buffer[0])  xxx handle the case of -0.0
-            else:
-                zero = False
-            if zero:
-                a.setlen(newlen, zero=True, overallocate=False)
-                return a
-            a.setlen(newlen, overallocate=False)
-            item = self.buffer[0]
+        def _repeat_single_item(self, a, start, repeat):
+            # <a performance hack>
+            assert isinstance(a, W_Array)
+            item = self.get_buffer()[0]
+            dstbuf = a.get_buffer()
             for r in range(start, repeat):
-                a.buffer[r] = item
-            return a
-        # </a performance hack>
-        a.setlen(newlen, overallocate=False)
-        for r in range(start, repeat):
-            for i in range(oldlen):
-                a.buffer[r * oldlen + i] = self.buffer[i]
-        return a
+                dstbuf[r] = item
 
     mytype.w_class = W_Array
     W_Array.constructor = W_Array
diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py 
b/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py
--- a/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py
+++ b/pypy/module/test_lib_pypy/ctypes_tests/test_buffers.py
@@ -24,6 +24,16 @@
         assert b[0] == "a"
         assert b[:] == "abc\0"
 
+    def test_from_buffer(self):
+        b1 = bytearray("abcde")
+        b = (c_char * 5).from_buffer(b1)
+        assert b[2] == "c"
+        #
+        b1 = bytearray("abcd")
+        b = c_int.from_buffer(b1)
+        assert b.value in (1684234849,   # little endian
+                           1633837924)   # big endian
+
     try:
         c_wchar
     except NameError:
diff --git a/rpython/jit/codewriter/test/test_call.py 
b/rpython/jit/codewriter/test/test_call.py
--- a/rpython/jit/codewriter/test/test_call.py
+++ b/rpython/jit/codewriter/test/test_call.py
@@ -281,6 +281,8 @@
 
 def test_elidable_kinds():
     from rpython.jit.backend.llgraph.runner import LLGraphCPU
+    from rpython.rlib.objectmodel import compute_hash
+    from rpython.rlib.rsiphash import enable_siphash24
 
     @jit.elidable
     def f1(n, m):
@@ -293,12 +295,17 @@
         if n > m:
             raise ValueError
         return n + m
+    @jit.elidable
+    def f4(n, m):
+        return compute_hash(str(n) + str(m))
 
     def f(n, m):
         a = f1(n, m)
         b = f2(n, m)
         c = f3(n, m)
-        return a + len(b) + c
+        d = f4(n, m)
+        enable_siphash24()
+        return a + len(b) + c + d
 
     rtyper = support.annotate(f, [7, 9])
     jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
@@ -309,7 +316,8 @@
     for index, expected in [
             (0, EffectInfo.EF_ELIDABLE_CANNOT_RAISE),
             (1, EffectInfo.EF_ELIDABLE_OR_MEMORYERROR),
-            (2, EffectInfo.EF_ELIDABLE_CAN_RAISE)]:
+            (2, EffectInfo.EF_ELIDABLE_CAN_RAISE),
+            (3, EffectInfo.EF_ELIDABLE_OR_MEMORYERROR)]:
         call_op = f_graph.startblock.operations[index]
         assert call_op.opname == 'direct_call'
         call_descr = cc.getcalldescr(call_op)
diff --git a/rpython/memory/gctransform/asmgcroot.py 
b/rpython/memory/gctransform/asmgcroot.py
--- a/rpython/memory/gctransform/asmgcroot.py
+++ b/rpython/memory/gctransform/asmgcroot.py
@@ -26,7 +26,6 @@
 
 class AsmGcRootFrameworkGCTransformer(BaseFrameworkGCTransformer):
     _asmgcc_save_restore_arguments = None
-    _seen_gctransformer_hint_close_stack = None
 
     def push_roots(self, hop, keep_current_args=False):
         livevars = self.get_livevars_for_roots(hop, keep_current_args)
@@ -50,29 +49,28 @@
         hop.genop("direct_call", [c_asm_nocollect, name])
 
     def gct_direct_call(self, hop):
+        # just a sanity check: if we find a fnptr with the hint on the
+        # _callable, then we'd also find the hint by looking only at the
+        # graph.  We'll actually change this graph only later, in
+        # start_transforming_graph().
         fnptr = hop.spaceop.args[0].value
         try:
             close_stack = fnptr._obj._callable._gctransformer_hint_close_stack_
         except AttributeError:
+            pass
+        else:
+            assert fnptr._obj.graph.func is fnptr._obj._callable
+        BaseFrameworkGCTransformer.gct_direct_call(self, hop)
+
+    def start_transforming_graph(self, graph):
+        try:
+            close_stack = graph.func._gctransformer_hint_close_stack_
+        except AttributeError:
             close_stack = False
         if close_stack:
-            self.handle_call_with_close_stack(hop)
-        else:
-            BaseFrameworkGCTransformer.gct_direct_call(self, hop)
+            self._transform_hint_close_stack(graph)
 
-    def handle_call_with_close_stack(self, hop):
-        fnptr = hop.spaceop.args[0].value
-        if self._seen_gctransformer_hint_close_stack is None:
-            self._seen_gctransformer_hint_close_stack = {}
-        if fnptr._obj.graph not in self._seen_gctransformer_hint_close_stack:
-            self._transform_hint_close_stack(fnptr)
-            self._seen_gctransformer_hint_close_stack[fnptr._obj.graph] = True
-        #
-        livevars = self.push_roots(hop)
-        self.default(hop)
-        self.pop_roots(hop, livevars)
-
-    def _transform_hint_close_stack(self, fnptr):
+    def _transform_hint_close_stack(self, graph):
         # We cannot easily pass variable amount of arguments of the call
         # across the call to the pypy_asm_stackwalk helper.  So we store
         # them away and restore them.  More precisely, we need to
@@ -83,8 +81,8 @@
         sradict = self._asmgcc_save_restore_arguments
         sra = []     # list of pointers to raw-malloced containers for args
         seen = {}
-        FUNC1 = lltype.typeOf(fnptr).TO
-        for TYPE in FUNC1.ARGS:
+        ARGS = [v.concretetype for v in graph.getargs()]
+        for TYPE in ARGS:
             if isinstance(TYPE, lltype.Ptr):
                 TYPE = llmemory.Address
             num = seen.get(TYPE, 0)
@@ -98,8 +96,10 @@
             sra.append(sradict[key])
         #
         # make a copy of the graph that will reload the values
-        graph = fnptr._obj.graph
         graph2 = copygraph(graph)
+        del graph2.func   # otherwise, start_transforming_graph() will
+                          # again transform graph2, and we get an
+                          # infinite loop
         #
         # edit the original graph to only store the value of the arguments
         block = Block(graph.startblock.inputargs)
@@ -116,17 +116,18 @@
                 SpaceOperation("bare_setfield", [c_p, c_item0, v_arg], v_void))
         #
         # call asm_stackwalk(graph2)
-        FUNC2 = lltype.FuncType([], FUNC1.RESULT)
+        RESULT = graph.getreturnvar().concretetype
+        FUNC2 = lltype.FuncType([], RESULT)
         fnptr2 = lltype.functionptr(FUNC2,
-                                    fnptr._obj._name + '_reload',
+                                    graph.name + '_reload',
                                     graph=graph2)
         c_fnptr2 = Constant(fnptr2, lltype.Ptr(FUNC2))
         HELPERFUNC = lltype.FuncType([lltype.Ptr(FUNC2),
-                                      ASM_FRAMEDATA_HEAD_PTR], FUNC1.RESULT)
+                                      ASM_FRAMEDATA_HEAD_PTR], RESULT)
         v_asm_stackwalk = varoftype(lltype.Ptr(HELPERFUNC), "asm_stackwalk")
         block.operations.append(
             SpaceOperation("cast_pointer", [c_asm_stackwalk], v_asm_stackwalk))
-        v_result = varoftype(FUNC1.RESULT)
+        v_result = varoftype(RESULT)
         block.operations.append(
             SpaceOperation("indirect_call", [v_asm_stackwalk, c_fnptr2,
                                              c_gcrootanchor,
diff --git a/rpython/memory/gctransform/transform.py 
b/rpython/memory/gctransform/transform.py
--- a/rpython/memory/gctransform/transform.py
+++ b/rpython/memory/gctransform/transform.py
@@ -201,6 +201,9 @@
         self.var_last_needed_in = None
         self.curr_block = None
 
+    def start_transforming_graph(self, graph):
+        pass    # for asmgcc.py
+
     def transform_graph(self, graph):
         if graph in self.minimal_transform:
             if self.minimalgctransformer:
@@ -210,6 +213,7 @@
         if graph in self.seen_graphs:
             return
         self.seen_graphs.add(graph)
+        self.start_transforming_graph(graph)
 
         self.links_to_split = {} # link -> vars to pop_alive across the link
 
diff --git a/rpython/rlib/test/test_rsignal.py 
b/rpython/rlib/test/test_rsignal.py
--- a/rpython/rlib/test/test_rsignal.py
+++ b/rpython/rlib/test/test_rsignal.py
@@ -1,4 +1,4 @@
-import os, py
+import os, py, errno
 from rpython.translator.c.test.test_genc import compile
 from rpython.rlib import rsignal
 
@@ -37,3 +37,24 @@
 def test_compile():
     fn = compile(test_simple, [])
     fn()
+
+def test_compile_wakeup_fd():
+    def fn():
+        rd, wr = os.pipe()
+        rsignal.pypysig_set_wakeup_fd(wr, False)
+        for i in range(3):
+            rsignal.pypysig_setflag(rsignal.SIGUSR1)
+            os.kill(os.getpid(), rsignal.SIGUSR1)
+            check(rsignal.SIGUSR1)
+            check(-1)
+            check(-1)
+        x = os.read(rd, 10)
+        assert x == chr(rsignal.SIGUSR1) * 3
+        #
+        rsignal.pypysig_set_wakeup_fd(rd, False)   # can't write there
+        os.kill(os.getpid(), rsignal.SIGUSR1)
+
+    fn = compile(fn, [], return_stderr=True)
+    stderr = fn()
+    assert stderr.endswith('Exception ignored when trying to write to the '
+                           'signal wakeup fd: Errno %d\n' % errno.EBADF)
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -539,7 +539,6 @@
     'decode_arg_def':       LLOp(canraise=(Exception,)),
     'getslice':             LLOp(canraise=(Exception,)),
     'check_and_clear_exc':  LLOp(),
-    'call_at_startup':      LLOp(canrun=True),
 
     'threadlocalref_addr':  LLOp(),                   # get (or make) addr of 
tl
     'threadlocalref_get':   LLOp(sideeffects=False),  # read field (no check)
diff --git a/rpython/rtyper/lltypesystem/opimpl.py 
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -742,9 +742,6 @@
 def op_gc_move_out_of_nursery(obj):
     return obj
 
-def op_call_at_startup(init_func):
-    pass    # do nothing
-
 # ____________________________________________________________
 
 def get_op_impl(opname):
diff --git a/rpython/translator/c/database.py b/rpython/translator/c/database.py
--- a/rpython/translator/c/database.py
+++ b/rpython/translator/c/database.py
@@ -60,7 +60,6 @@
         self.completed = False
 
         self.instrument_ncounter = 0
-        self.call_at_startup = set()
 
     def gettypedefnode(self, T, varlength=None):
         if varlength is None:
diff --git a/rpython/translator/c/funcgen.py b/rpython/translator/c/funcgen.py
--- a/rpython/translator/c/funcgen.py
+++ b/rpython/translator/c/funcgen.py
@@ -941,18 +941,3 @@
                 cdecl(typename, ''),
                 self.expr(op.args[0]),
                 self.expr(op.result))
-
-    def OP_CALL_AT_STARTUP(self, op):
-        c = op.args[0]
-        if not isinstance(c, Constant):
-            # Bah, maybe it comes from a same_as(const) just before...
-            # Can occur if running without backendopts
-            for op1 in self._current_block.operations:
-                if op1.result is op.args[0]:
-                    assert op1.opname == "same_as"
-                    c = op1.args[0]
-                    break
-            assert isinstance(c, Constant)
-        func = self.expr(c)
-        self.db.call_at_startup.add(func)
-        return '/* call_at_startup %s */' % (func,)
diff --git a/rpython/translator/c/src/signals.c 
b/rpython/translator/c/src/signals.c
--- a/rpython/translator/c/src/signals.c
+++ b/rpython/translator/c/src/signals.c
@@ -2,6 +2,7 @@
 
 #include <limits.h>
 #include <stdlib.h>
+#include <errno.h>
 #ifdef _WIN32
 #include <process.h>
 #include <io.h>
@@ -82,27 +83,51 @@
       }
 }
 
+static void write_str(int fd, const char *p)
+{
+    int i = 0;
+    while (p[i] != '\x00')
+        i++;
+    (void)write(fd, p, i);
+}
+
 static void signal_setflag_handler(int signum)
 {
     pypysig_pushback(signum);
 
-    if (wakeup_fd != -1) 
-      {
+    /* Warning, this logic needs to be async-signal-safe */
+    if (wakeup_fd != -1) {
 #ifndef _WIN32
         ssize_t res;
 #else
         int res;
 #endif
-       if (wakeup_with_nul_byte)
-       {
-           res = write(wakeup_fd, "\0", 1);
-       } else {
-           unsigned char byte = (unsigned char)signum;
-           res = write(wakeup_fd, &byte, 1);
-       }
-
-        /* the return value is ignored here */
-      }
+        int old_errno = errno;
+     retry:
+        if (wakeup_with_nul_byte) {
+            res = write(wakeup_fd, "\0", 1);
+        } else {
+            unsigned char byte = (unsigned char)signum;
+            res = write(wakeup_fd, &byte, 1);
+        }
+        if (res < 0) {
+            unsigned int e = (unsigned int)errno;
+            char c[27], *p;
+            if (e == EINTR)
+                goto retry;
+            write_str(2, "Exception ignored when trying to write to the "
+                         "signal wakeup fd: Errno ");
+            p = c + sizeof(c);
+            *--p = 0;
+            *--p = '\n';
+            do {
+                *--p = '0' + e % 10;
+                e /= 10;
+            } while (e != 0);
+            write_str(2, p);
+        }
+        errno = old_errno;
+    }
 }
 
 void pypysig_setflag(int signum)
diff --git a/rpython/translator/c/test/test_standalone.py 
b/rpython/translator/c/test/test_standalone.py
--- a/rpython/translator/c/test/test_standalone.py
+++ b/rpython/translator/c/test/test_standalone.py
@@ -1063,23 +1063,39 @@
         assert out.strip() == expected
 
     def test_call_at_startup(self):
-        from rpython.rtyper.lltypesystem import lltype
-        from rpython.rtyper.lltypesystem.lloperation import llop
-        from rpython.rtyper.annlowlevel import llhelper
+        from rpython.rtyper.extregistry import ExtRegistryEntry
+
         class State:
             seen = 0
         state = State()
         def startup():
             state.seen += 1
-        F = lltype.Ptr(lltype.FuncType([], lltype.Void))
+        def enablestartup():
+            "NOT_RPYTHON"
         def entry_point(argv):
             state.seen += 100
             assert state.seen == 101
             print 'ok'
-            ll = llhelper(F, startup)
-            llop.call_at_startup(lltype.Void, ll)
+            enablestartup()
             return 0
 
+        class Entry(ExtRegistryEntry):
+            _about_ = enablestartup
+
+            def compute_result_annotation(self):
+                bk = self.bookkeeper
+                s_callable = bk.immutablevalue(startup)
+                key = (enablestartup,)
+                bk.emulate_pbc_call(key, s_callable, [])
+
+            def specialize_call(self, hop):
+                hop.exception_cannot_occur()
+                bk = hop.rtyper.annotator.bookkeeper
+                s_callable = bk.immutablevalue(startup)
+                r_callable = hop.rtyper.getrepr(s_callable)
+                ll_init = r_callable.get_unique_llfn().value
+                bk.annotator.translator._call_at_startup.append(ll_init)
+
         t, cbuilder = self.compile(entry_point)
         out = cbuilder.cmdexec('')
         assert out.strip() == 'ok'
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to