[pypy-commit] pypy fix-bytearray-complexity: Add to RPython support for __getitem__, __setitem, __getslice__, __setslice__, and __len__

waedt noreply at buildbot.pypy.org
Mon Jun 2 19:47:11 CEST 2014


Author: Tyler Wade <wayedt at gmail.com>
Branch: fix-bytearray-complexity
Changeset: r71878:5e3423ac75bf
Date: 2014-06-01 05:26 -0500
http://bitbucket.org/pypy/pypy/changeset/5e3423ac75bf/

Log:	Add to RPython support for __getitem__, __setitem, __getslice__,
	__setslice__, and __len__

diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -719,6 +719,14 @@
             return super(thistype, pair(ins1, ins2)).improve()
 
 
+class __extend__(pairtype(SomeInstance, SomeObject)):
+    def getitem((s_ins, s_idx)):
+        return s_ins._emulate_call("__getitem__", s_idx)
+
+    def setitem((s_ins, s_idx), s_value):
+        return s_ins._emulate_call("__setitem__", s_idx, s_value)
+
+
 class __extend__(pairtype(SomeIterator, SomeIterator)):
 
     def union((iter1, iter2)):
diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -3937,6 +3937,78 @@
         s = a.build_types(fn, [int])
         assert isinstance(s, annmodel.SomeInteger)
 
+    def test_instance_getitem(self):
+        class A(object):
+            def __getitem__(self, i):
+                return i * i
+
+        def fn(i):
+            a = A()
+            return a[i]
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [int])
+        assert len(a.translator.graphs) == 2 # fn, __getitem__
+        assert isinstance(s, annmodel.SomeInteger)
+
+    def test_instance_setitem(self):
+        class A(object):
+            def __setitem__(self, i, v):
+                self.value = i * v
+
+        def fn(i, v):
+            a = A()
+            a[i] = v
+            return a.value
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [int, int])
+        assert len(a.translator.graphs) == 2 # fn, __setitem__
+        assert isinstance(s, annmodel.SomeInteger)
+
+    def test_instance_getslice(self):
+        class A(object):
+            def __getslice__(self, stop, start):
+                return "Test"[stop:start]
+
+        def fn():
+            a = A()
+            return a[0:2]
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [])
+        assert len(a.translator.graphs) == 2 # fn, __getslice__
+        assert isinstance(s, annmodel.SomeString)
+
+    def test_instance_setslice(self):
+        class A(object):
+            def __setslice__(self, stop, start, value):
+                self.value = value
+
+        def fn():
+            a = A()
+            a[0:2] = '00'
+            return a.value
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [])
+        assert len(a.translator.graphs) == 2 # fn, __setslice__
+        assert isinstance(s, annmodel.SomeString)
+
+    def test_instance_len(self):
+        class A(object):
+            def __len__(self):
+                return 0
+
+        def fn():
+            a = A()
+            return len(a)
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(fn, [])
+        assert len(a.translator.graphs) == 2 # fn, __len__
+        assert isinstance(s, annmodel.SomeInteger)
+
     def test_reversed(self):
         def fn(n):
             for elem in reversed([1, 2, 3, 4, 5]):
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -683,19 +683,27 @@
         if not self.can_be_None:
             s.const = True
 
+    def _emulate_call(self, meth_name, *args_s):
+        bk = getbookkeeper()
+        s_attr = self._true_getattr(meth_name)
+        # record for calltables
+        bk.emulate_pbc_call(bk.position_key, s_attr, args_s)
+        return s_attr.call(simple_args(args_s))
+
     def iter(self):
-        s_iterable = self._true_getattr('__iter__')
-        bk = getbookkeeper()
-        # record for calltables
-        bk.emulate_pbc_call(bk.position_key, s_iterable, [])
-        return s_iterable.call(simple_args([]))
+        return self._emulate_call('__iter__')
 
     def next(self):
-        s_next = self._true_getattr('next')
-        bk = getbookkeeper()
-        # record for calltables
-        bk.emulate_pbc_call(bk.position_key, s_next, [])
-        return s_next.call(simple_args([]))
+        return self._emulate_call('next')
+
+    def len(self):
+        return self._emulate_call('__len__')
+
+    def getslice(self, s_start, s_stop):
+        return self._emulate_call('__getslice__', s_start, s_stop)
+
+    def setslice(self, s_start, s_stop, s_iterable):
+        return self._emulate_call('__setslice__', s_start, s_stop, s_iterable)
 
 class __extend__(SomeBuiltin):
     def simple_call(self, *args):
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -12,6 +12,9 @@
     def getlength(self):
         raise NotImplementedError
 
+    def __len__(self):
+        return self.getlength()
+
     def as_str(self):
         "Returns an interp-level string with the whole content of the buffer."
         # May be overridden.
@@ -21,14 +24,23 @@
         "Returns the index'th character in the buffer."
         raise NotImplementedError   # Must be overriden.  No bounds checks.
 
+    def __getitem__(self, i):
+        return self.getitem(i)
+
     def getslice(self, start, stop, step, size):
         # May be overridden.  No bounds checks.
         return ''.join([self.getitem(i) for i in range(start, stop, step)])
 
+    def __getslice__(self, start, stop):
+        return self.getslice(start, stop, 1, stop - start)
+
     def setitem(self, index, char):
         "Write a character into the buffer."
         raise NotImplementedError   # Must be overriden.  No bounds checks.
 
+    def __setitem__(self, i, char):
+        return self.setitem(i, char)
+
     def setslice(self, start, string):
         # May be overridden.  No bounds checks.
         for i in range(len(string)):
diff --git a/rpython/rlib/test/test_buffer.py b/rpython/rlib/test/test_buffer.py
--- a/rpython/rlib/test/test_buffer.py
+++ b/rpython/rlib/test/test_buffer.py
@@ -4,7 +4,10 @@
 def test_string_buffer():
     buf = StringBuffer('hello world')
     assert buf.getitem(4) == 'o'
+    assert buf.getitem(4) == buf[4]
     assert buf.getlength() == 11
+    assert buf.getlength() == len(buf)
     assert buf.getslice(1, 6, 1, 5) == 'ello '
+    assert buf.getslice(1, 6, 1, 5) == buf[1:6]
     assert buf.getslice(1, 6, 2, 3) == 'el '
     assert buf.as_str() == 'hello world'
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -7,6 +7,7 @@
 from rpython.rtyper.lltypesystem.lltype import Void
 from rpython.rtyper.rmodel import Repr, getgcflavor, inputconst
 from rpython.rlib.objectmodel import UnboxedValue
+from rpython.tool.pairtype import pairtype
 
 
 class FieldListAccessor(object):
@@ -390,7 +391,7 @@
         raise NotImplementedError
 
     def _emulate_call(self, hop, meth_name):
-        vinst, = hop.inputargs(self)
+        vinst = hop.args_v[0]
         clsdef = hop.args_s[0].classdef
         s_unbound_attr = clsdef.find_attribute(meth_name).getvalue()
         s_attr = clsdef.lookup_filter(s_unbound_attr, meth_name,
@@ -402,10 +403,10 @@
         r_method = self.rtyper.getrepr(s_attr)
         r_method.get_method_from_instance(self, vinst, hop.llops)
         hop2 = hop.copy()
-        hop2.spaceop = op.simple_call(hop.spaceop.args[0])
+        hop2.spaceop = op.simple_call(*hop.spaceop.args)
         hop2.spaceop.result = hop.spaceop.result
-        hop2.args_r = [r_method]
-        hop2.args_s = [s_attr]
+        hop2.args_r[0] = r_method
+        hop2.args_s[0] = s_attr
         return hop2.dispatch()
 
     def rtype_iter(self, hop):
@@ -414,6 +415,15 @@
     def rtype_next(self, hop):
         return self._emulate_call(hop, 'next')
 
+    def rtype_getslice(self, hop):
+        return self._emulate_call(hop, "__getslice__")
+
+    def rtype_setslice(self, hop):
+        return self._emulate_call(hop, "__setslice__")
+
+    def rtype_len(self, hop):
+        return self._emulate_call(hop, "__len__")
+
     def ll_str(self, i):
         raise NotImplementedError
 
@@ -460,6 +470,16 @@
             if len(seen) == oldlength:
                 break
 
+
+class __extend__(pairtype(AbstractInstanceRepr, Repr)):
+    def rtype_getitem((r_ins, r_obj), hop):
+        return r_ins._emulate_call(hop, "__getitem__")
+
+    def rtype_setitem((r_ins, r_obj), hop):
+        return r_ins._emulate_call(hop, "__setitem__")
+
+
+
 # ____________________________________________________________
 
 def rtype_new_instance(rtyper, classdef, llops, classcallhop=None):
diff --git a/rpython/rtyper/test/test_rclass.py b/rpython/rtyper/test/test_rclass.py
--- a/rpython/rtyper/test/test_rclass.py
+++ b/rpython/rtyper/test/test_rclass.py
@@ -1193,6 +1193,69 @@
         assert self.interpret(f, [True]) == f(True)
         assert self.interpret(f, [False]) == f(False)
 
+    def test_indexing(self):
+        class A(object):
+            def __init__(self, data):
+                self.data = data
+
+            def __getitem__(self, i):
+                return self.data[i]
+
+            def __setitem__(self, i, v):
+                self.data[i] = v
+
+            def __getslice__(self, start, stop):
+                assert start >= 0
+                assert stop >= 0
+                return self.data[start:stop]
+
+            def __setslice__(self, start, stop, v):
+                assert start >= 0
+                assert stop >= 0
+                i = 0
+                for n in range(start, stop):
+                    self.data[n] = v[i]
+                    i += 1
+
+        def getitem(i):
+            a = A("abcdefg")
+            return a[i]
+
+        def setitem(i, v):
+            a = A([0] * 5)
+            a[i] = v
+            return a[i]
+
+        def getslice(start, stop):
+            a = A([1, 2, 3, 4, 5, 6])
+            sum = 0
+            for i in a[start:stop]:
+                sum += i
+            return sum
+
+        def setslice(start, stop, i):
+            a = A([0] * stop)
+            a[start:stop] = range(start, stop)
+            return a[i]
+
+        assert self.interpret(getitem, [0]) == getitem(0)
+        assert self.interpret(getitem, [1]) == getitem(1)
+        assert self.interpret(setitem, [0, 5]) == setitem(0, 5)
+        assert self.interpret(getslice, [0, 4]) == getslice(0, 4)
+        assert self.interpret(getslice, [1, 4]) == getslice(1, 4)
+        assert self.interpret(setslice, [4, 6, 5]) == setslice(4, 6, 5)
+
+    def test_len(self):
+        class A(object):
+            def __len__(self):
+                return 5
+
+        def fn():
+            a = A()
+            return len(a)
+
+        assert self.interpret(fn, []) == fn()
+
     def test_init_with_star_args(self):
         class Base(object):
             def __init__(self, a, b):


More information about the pypy-commit mailing list