Commits

Carl Friedrich Bolz committed caa63b8

implement a fast path for list.pop() (without arguments)

Comments (0)

Files changed (3)

pypy/objspace/std/listobject.py

         May raise IndexError."""
         return self.strategy.pop(self, index)
 
+    def pop_end(self):
+        """ Pop the last element from the list."""
+        return self.strategy.pop_end(self)
+
     def setitem(self, index, w_item):
         """Inserts a wrapped item at the given (unwrapped) index.
         May raise IndexError."""
     def pop(self, w_list, index):
         raise NotImplementedError
 
+    def pop_end(self, w_list):
+        return self.pop(w_list, self.length(w_list) - 1)
+
     def setitem(self, w_list, index, w_item):
         raise NotImplementedError
 
         pass
 
     def pop(self, w_list, index):
-        # will not be called becuase IndexError was already raised in
+        # will not be called because IndexError was already raised in
         # list_pop__List_ANY
         raise IndexError
 
         self.switch_to_integer_strategy(w_list)
         w_list.deleteslice(start, step, slicelength)
 
+    def pop_end(self, w_list):
+        start, step, length = self.unerase(w_list.lstorage)
+        w_result = self.wrap(start + (length - 1) * step)
+        new = self.erase((start, step, length - 1))
+        w_list.lstorage = new
+        return w_result
+
     def pop(self, w_list, index):
         l = self.unerase(w_list.lstorage)
         start = l[0]
         step = l[1]
         length = l[2]
         if index == 0:
-            r = self.getitem(w_list, index)
+            w_result = self.wrap(start)
             new = self.erase((start + step, step, length - 1))
             w_list.lstorage = new
-            return r
+            return w_result
         elif index == length - 1:
-            r = self.getitem(w_list, index)
-            new = self.erase((start, step, length - 1))
-            w_list.lstorage = new
-            return r
+            return self.pop_end(w_list)
         else:
             self.switch_to_integer_strategy(w_list)
             return w_list.pop(index)
             assert start >= 0 # annotator hint
             del items[start:]
 
+    def pop_end(self, w_list):
+        l = self.unerase(w_list.lstorage)
+        return self.wrap(l.pop())
+
     def pop(self, w_list, index):
         l = self.unerase(w_list.lstorage)
         # not sure if RPython raises IndexError on pop
     w_list.extend(w_other)
     return space.w_None
 
-# note that the default value will come back wrapped!!!
-def list_pop__List_ANY(space, w_list, w_idx=-1):
+# default of w_idx is space.w_None (see listtype.py)
+def list_pop__List_ANY(space, w_list, w_idx):
     length = w_list.length()
     if length == 0:
         raise OperationError(space.w_IndexError,
                              space.wrap("pop from empty list"))
+    # clearly differentiate between list.pop() and list.pop(index)
+    if space.is_w(w_idx, space.w_None):
+        return w_list.pop_end() # cannot raise because list is not empty
     if space.isinstance_w(w_idx, space.w_float):
         raise OperationError(space.w_TypeError,
             space.wrap("integer argument expected, got float")

pypy/objspace/std/listtype.py

 list_extend   = SMM('extend', 2,
                     doc='L.extend(iterable) -- extend list by appending'
                         ' elements from the iterable')
-list_pop      = SMM('pop',    2, defaults=(-1,),
+list_pop      = SMM('pop',    2, defaults=(None,),
                     doc='L.pop([index]) -> item -- remove and return item at'
                         ' index (default last)')
 list_remove   = SMM('remove', 2,

pypy/objspace/std/test/test_liststrategies.py

 from pypy.objspace.std.listobject import W_ListObject, EmptyListStrategy, ObjectListStrategy, IntegerListStrategy, StringListStrategy, RangeListStrategy, make_range_list
+from pypy.objspace.std import listobject
 from pypy.objspace.std.test.test_listobject import TestW_ListObject
 
 from pypy.conftest import gettestobjspace
 
         l = make_range_list(self.space, 1,3,7)
         assert isinstance(l.strategy, RangeListStrategy)
+        v = l.pop(0)
+        assert self.space.eq_w(v, self.space.wrap(1))
+        assert isinstance(l.strategy, RangeListStrategy)
+        v = l.pop(l.length() - 1)
+        assert self.space.eq_w(v, self.space.wrap(19))
+        assert isinstance(l.strategy, RangeListStrategy)
+        v = l.pop_end()
+        assert self.space.eq_w(v, self.space.wrap(16))
+        assert isinstance(l.strategy, RangeListStrategy)
+
+        l = make_range_list(self.space, 1,3,7)
+        assert isinstance(l.strategy, RangeListStrategy)
         l.append(self.space.wrap("string"))
         assert isinstance(l.strategy, ObjectListStrategy)
 
         assert space.listview_str(w_l) == ["a", "b", "c"]
         assert space.listview_str(w_l2) == ["a", "b", "c"]
 
+    def test_pop_without_argument_is_fast(self):
+        space = self.space
+        w_l = W_ListObject(space, [space.wrap(1), space.wrap(2), space.wrap(3)])
+        w_l.pop = None
+        w_res = listobject.list_pop__List_ANY(space, w_l, space.w_None) # does not crash
+        assert space.unwrap(w_res) == 3
+
 
 class TestW_ListStrategiesDisabled:
     def setup_class(cls):