Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r91038:423026acc94c
Date: 2017-04-11 19:08 +0200
http://bitbucket.org/pypy/pypy/changeset/423026acc94c/

Log:    Fix co_freevars and co_cellvars to always list variables in
        alphabetical order. It seems that some bytecode hacks depend on it
        (or at least getting a consistent order even between different
        functions).

diff --git a/pypy/interpreter/astcompiler/assemble.py 
b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -7,6 +7,7 @@
 from pypy.interpreter.astcompiler import ast, misc, symtable
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.pycode import PyCode
+from pypy.interpreter.miscutils import string_sort
 from pypy.tool import stdlib_opcode as ops
 
 
@@ -138,9 +139,12 @@
 
 
 def _make_index_dict_filter(syms, flag):
+    names = syms.keys()
+    string_sort(names)   # return cell vars in alphabetical order
     i = 0
     result = {}
-    for name, scope in syms.iteritems():
+    for name in names:
+        scope = syms[name]
         if scope == flag:
             result[name] = i
             i += 1
@@ -170,6 +174,7 @@
         self.var_names = _list_to_dict(scope.varnames)
         self.cell_vars = _make_index_dict_filter(scope.symbols,
                                                  symtable.SCOPE_CELL)
+        string_sort(scope.free_vars)    # return free vars in alphabetical 
order
         self.free_vars = _list_to_dict(scope.free_vars, len(self.cell_vars))
         self.w_consts = space.newdict()
         self.argcount = 0
diff --git a/pypy/interpreter/astcompiler/symtable.py 
b/pypy/interpreter/astcompiler/symtable.py
--- a/pypy/interpreter/astcompiler/symtable.py
+++ b/pypy/interpreter/astcompiler/symtable.py
@@ -36,7 +36,7 @@
         self.roles = {}
         self.varnames = []
         self.children = []
-        self.free_vars = []
+        self.free_vars = []    # a bag of names: the order doesn't matter here
         self.temp_name_counter = 1
         self.has_exec = False
         self.has_free = False
diff --git a/pypy/interpreter/miscutils.py b/pypy/interpreter/miscutils.py
--- a/pypy/interpreter/miscutils.py
+++ b/pypy/interpreter/miscutils.py
@@ -2,6 +2,9 @@
 Miscellaneous utilities.
 """
 
+from rpython.rlib.listsort import make_timsort_class
+
+
 class ThreadLocals:
     """Pseudo thread-local storage, for 'space.threadlocals'.
     This is not really thread-local at all; the intention is that the PyPy
@@ -53,3 +56,15 @@
             def set(self, key, value):
                 self._dict[key] = value
         return FakeWeakValueDict()
+
+
+_StringBaseTimSort = make_timsort_class()
+
+class StringSort(_StringBaseTimSort):
+    def lt(self, a, b):
+        return a < b
+
+def string_sort(lst):
+    """Sort a (resizable) list of strings."""
+    sorter = StringSort(lst, len(lst))
+    sorter.sort()
diff --git a/pypy/interpreter/test/test_compiler.py 
b/pypy/interpreter/test/test_compiler.py
--- a/pypy/interpreter/test/test_compiler.py
+++ b/pypy/interpreter/test/test_compiler.py
@@ -787,6 +787,32 @@
         else:
             assert l1 == l2 == l3 == l4 == [1, 3, 2, 4]
 
+    def test_freevars_order(self):
+        # co_cellvars and co_freevars are guaranteed to appear in
+        # alphabetical order.  See CPython Issue #15368 (which does
+        # not come with tests).
+        source = """if 1:
+        def f1(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15):
+            def g1():
+                return (x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15)
+            return g1
+        def f2(x15,x14,x13,x12,x11,x10,x9,x8,x7,x6,x5,x4,x3,x2,x1):
+            def g2():
+                return (x15,x14,x13,x12,x11,x10,x9,x8,x7,x6,x5,x4,x3,x2,x1)
+            return g2
+        c1 = f1(*range(15)).__code__.co_freevars
+        c2 = f2(*range(15)).__code__.co_freevars
+        r1 = f1.__code__.co_cellvars
+        r2 = f2.__code__.co_cellvars
+        """
+        d = {}
+        exec(source, d)
+        assert d['c1'] == d['c2']
+        # the test above is important for a few bytecode hacks,
+        # but actually we get them in alphabetical order, so check that:
+        assert d['c1'] == tuple(sorted(d['c1']))
+        assert d['r1'] == d['r2'] == d['c1']
+
 
 ##class TestPythonAstCompiler(BaseTestCompiler):
 ##    def setup_method(self, method):
diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py
--- a/pypy/objspace/std/listobject.py
+++ b/pypy/objspace/std/listobject.py
@@ -23,6 +23,7 @@
     WrappedDefault, applevel, interp2app, unwrap_spec)
 from pypy.interpreter.signature import Signature
 from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.miscutils import StringSort
 from pypy.objspace.std.bytesobject import W_BytesObject
 from pypy.objspace.std.floatobject import W_FloatObject
 from pypy.objspace.std.intobject import W_IntObject
@@ -2060,7 +2061,6 @@
 IntBaseTimSort = make_timsort_class()
 FloatBaseTimSort = make_timsort_class()
 IntOrFloatBaseTimSort = make_timsort_class()
-StringBaseTimSort = make_timsort_class()
 UnicodeBaseTimSort = make_timsort_class()
 
 
@@ -2097,11 +2097,6 @@
         return fa < fb
 
 
-class StringSort(StringBaseTimSort):
-    def lt(self, a, b):
-        return a < b
-
-
 class UnicodeSort(UnicodeBaseTimSort):
     def lt(self, a, b):
         return a < b
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -1043,7 +1043,7 @@
 
 
 def create_all_slots(w_self, hasoldstylebase, w_bestbase, force_new_layout):
-    from pypy.objspace.std.listobject import StringSort
+    from pypy.interpreter.miscutils import string_sort
 
     base_layout = w_bestbase.layout
     index_next_extra_slot = base_layout.nslots
@@ -1077,8 +1077,7 @@
             else:
                 newslotnames.append(slot_name)
         # Sort the list of names collected so far
-        sorter = StringSort(newslotnames, len(newslotnames))
-        sorter.sort()
+        string_sort(newslotnames)
         # Try to create all slots in order.  The creation of some of
         # them might silently fail; then we delete the name from the
         # list.  At the end, 'index_next_extra_slot' has been advanced
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to