Author: Carl Friedrich Bolz-Tereick <[email protected]>
Branch: py3.6
Changeset: r98553:193b979f5bec
Date: 2020-01-17 22:04 +0100
http://bitbucket.org/pypy/pypy/changeset/193b979f5bec/

Log:    re-implement BUILD_LIST_FROM_ARG in PyPy3

        (the comment explaining why it was hard is bogus: iterators have
        __length_hint__ too)

diff --git a/pypy/TODO b/pypy/TODO
--- a/pypy/TODO
+++ b/pypy/TODO
@@ -3,10 +3,4 @@
 * run coverage against the parser/astbuilder/astcompiler: it's probably full of
 dead code because the grammar changed
 
-* optimize W_UnicodeObject, right now it stores both an unicode and an utf8
-version of the same string
-
-* re-enable BUILD_LIST_FROM_ARG: see the comment in astcompiler/codegen.py in
-ast.ListComp.build_container
-
 * review use of std_decode_utf8, we probably do not want to be using it
diff --git a/pypy/interpreter/astcompiler/codegen.py 
b/pypy/interpreter/astcompiler/codegen.py
--- a/pypy/interpreter/astcompiler/codegen.py
+++ b/pypy/interpreter/astcompiler/codegen.py
@@ -111,8 +111,8 @@
 
 class __extend__(ast.GeneratorExp):
 
-    def build_container(self, codegen):
-        pass
+    def build_container_and_load_iter(self, codegen):
+        codegen.comprehension_load_iter()
 
     def get_generators(self):
         return self.generators
@@ -125,12 +125,18 @@
 
 class __extend__(ast.ListComp):
 
-    def build_container(self, codegen):
-        # XXX: this is suboptimal: if we use BUILD_LIST_FROM_ARG it's faster
-        # because it preallocates the list; however, we cannot use it because
-        # at this point we only have the iterator, not the original iterable
-        # object
-        codegen.emit_op_arg(ops.BUILD_LIST, 0)
+    def build_container_and_load_iter(self, codegen):
+        single = False
+        if len(self.generators) == 1:
+            gen, = self.generators
+            if not gen.ifs:
+                single = True
+        if single:
+            codegen.comprehension_load_iter()
+            codegen.emit_op(ops.BUILD_LIST_FROM_ARG)
+        else:
+            codegen.emit_op_arg(ops.BUILD_LIST, 0)
+            codegen.comprehension_load_iter()
 
     def get_generators(self):
         return self.generators
@@ -142,8 +148,9 @@
 
 class __extend__(ast.SetComp):
 
-    def build_container(self, codegen):
+    def build_container_and_load_iter(self, codegen):
         codegen.emit_op_arg(ops.BUILD_SET, 0)
+        codegen.comprehension_load_iter()
 
     def get_generators(self):
         return self.generators
@@ -155,8 +162,9 @@
 
 class __extend__(ast.DictComp):
 
-    def build_container(self, codegen):
+    def build_container_and_load_iter(self, codegen):
         codegen.emit_op_arg(ops.BUILD_MAP, 0)
+        codegen.comprehension_load_iter()
 
     def get_generators(self):
         return self.generators
@@ -1555,10 +1563,7 @@
         anchor = self.new_block()
         gen = generators[gen_index]
         assert isinstance(gen, ast.comprehension)
-        if gen_index == 0:
-            self.argcount = 1
-            self.emit_op_arg(ops.LOAD_FAST, 0)
-        else:
+        if gen_index > 0:
             gen.iter.walkabout(self)
             self.emit_op(ops.GET_ITER)
         self.use_next_block(start)
@@ -1588,10 +1593,7 @@
         b_anchor = self.new_block()
         gen = generators[gen_index]
         assert isinstance(gen, ast.comprehension)
-        if gen_index == 0:
-            self.argcount = 1
-            self.emit_op_arg(ops.LOAD_FAST, 0)
-        else:
+        if gen_index > 0:
             gen.iter.walkabout(self)
             self.emit_op(ops.GET_AITER)
             self.load_const(self.space.w_None)
@@ -1888,12 +1890,16 @@
 class ComprehensionCodeGenerator(AbstractFunctionCodeGenerator):
 
     def _compile(self, node):
+        self.argcount = 1
         assert isinstance(node, ast.expr)
         self.update_position(node.lineno)
-        node.build_container(self)
+        node.build_container_and_load_iter(self)
         self._comp_generator(node, node.get_generators(), 0)
         self._end_comp()
 
+    def comprehension_load_iter(self):
+        self.emit_op_arg(ops.LOAD_FAST, 0)
+
     def _end_comp(self):
         self.emit_op(ops.RETURN_VALUE)
 
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py 
b/pypy/interpreter/astcompiler/test/test_compiler.py
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -1318,15 +1318,24 @@
     # BUILD_LIST_FROM_ARG is PyPy specific
     @py.test.mark.skipif('config.option.runappdirect')
     def test_build_list_from_arg_length_hint(self):
-        py3k_skip('XXX: BUILD_LIST_FROM_ARG list comps are genexps on py3k')
         hint_called = [False]
         class Foo(object):
+            def __iter__(self):
+                return FooIter()
+        class FooIter:
+            def __init__(self):
+                self.i = 0
             def __length_hint__(self):
                 hint_called[0] = True
                 return 5
             def __iter__(self):
-                for i in range(5):
-                    yield i
+                return self
+            def __next__(self):
+                if self.i < 5:
+                    res = self.i
+                    self.i += 1
+                    return res
+                raise StopIteration
         l = [a for a in Foo()]
         assert hint_called[0]
         assert l == list(range(5))
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to