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