[pypy-commit] pypy virtual-arguments: replace the used_keywords list of bools with a much shorter list

cfbolz noreply at buildbot.pypy.org
Mon Apr 23 11:54:28 CEST 2012


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: virtual-arguments
Changeset: r54656:075a2619161a
Date: 2012-04-22 11:37 +0200
http://bitbucket.org/pypy/pypy/changeset/075a2619161a/

Log:	replace the used_keywords list of bools with a much shorter list
	that is as long as the number of potentially missing arguments the
	content of the list is an index into keywords_w

	the idea is that scope_w should not be passed into a residual call.
	that would make it escape, which is bad, scope_w is part of an
	(ideally virtual) frame later

diff --git a/pypy/interpreter/argument.py b/pypy/interpreter/argument.py
--- a/pypy/interpreter/argument.py
+++ b/pypy/interpreter/argument.py
@@ -296,35 +296,45 @@
 
         # handle keyword arguments
         num_remainingkwds = 0
-        used_keywords = None
         keywords_w = self.keywords_w
+        kwds_mapping = None
         if num_kwds:
-            used_keywords = [False] * num_kwds
+            # kwds_mapping maps target indexes in the scope (minus input_argcount)
+            # to positions in the keywords_w list
+            kwds_mapping = [-1] * (co_argcount - input_argcount)
             # match the keywords given at the call site to the argument names
             # the called function takes
+            # this function must not take a scope_w, to make the scope not
+            # escape
             num_remainingkwds = _match_keywords(
                     signature, blindargs, input_argcount, keywords,
-                    keywords_w, scope_w, used_keywords,
-                    self._jit_few_keywords)
+                    kwds_mapping, self._jit_few_keywords)
             if num_remainingkwds:
                 if w_kwds is not None:
                     # collect extra keyword arguments into the **kwarg
                     _collect_keyword_args(
                             self.space, keywords, keywords_w, w_kwds,
-                            used_keywords, self.keyword_names_w, self._jit_few_keywords)
+                            kwds_mapping, self.keyword_names_w, self._jit_few_keywords)
                 else:
                     if co_argcount == 0:
                         raise ArgErrCount(avail, num_kwds, signature, defaults_w, 0)
                     raise ArgErrUnknownKwds(self.space, num_remainingkwds, keywords,
-                                            used_keywords, self.keyword_names_w)
+                                            kwds_mapping, self.keyword_names_w)
 
-        # check for missing arguments and fill them with defaults, if available
+        # check for missing arguments and fill them from the kwds,
+        # or with defaults, if available
         missing = 0
         if input_argcount < co_argcount:
             def_first = co_argcount - (0 if defaults_w is None else len(defaults_w))
+            j = 0
+            kwds_index = -1
             for i in range(input_argcount, co_argcount):
-                if scope_w[i] is not None:
-                    continue
+                if kwds_mapping is not None:
+                    kwds_index = kwds_mapping[j]
+                    j += 1
+                    if kwds_index != -1:
+                        scope_w[i] = keywords_w[kwds_index]
+                        continue
                 defnum = i - def_first
                 if defnum >= 0:
                     scope_w[i] = defaults_w[defnum]
@@ -441,10 +451,10 @@
         i += 1
 
 @jit.look_inside_iff(
-    lambda signature, blindargs, input_argcount, keywords, keywords_w,
-           scope_w, used_keywords, jiton: jiton)
+    lambda signature, blindargs, input_argcount, keywords,
+           scope_w, kwds_mapping, jiton: jiton)
 def _match_keywords(signature, blindargs, input_argcount,
-                    keywords, keywords_w, scope_w, used_keywords, _):
+                    keywords, kwds_mapping, _):
     # letting JIT unroll the loop is *only* safe if the callsite didn't
     # use **args because num_kwds can be arbitrarily large otherwise.
     num_kwds = num_remainingkwds = len(keywords)
@@ -466,22 +476,25 @@
             if blindargs <= j:
                 raise ArgErrMultipleValues(name)
         else:
-            assert scope_w[j] is None
-            scope_w[j] = keywords_w[i]
-            used_keywords[i] = True # mark as used
+            kwds_mapping[j - input_argcount] = i # map to the right index
             num_remainingkwds -= 1
     return num_remainingkwds
 
 @jit.look_inside_iff(
-    lambda space, keywords, keywords_w, w_kwds, used_keywords,
+    lambda space, keywords, keywords_w, w_kwds, kwds_mapping,
         keyword_names_w, jiton: jiton)
-def _collect_keyword_args(space, keywords, keywords_w, w_kwds, used_keywords,
+def _collect_keyword_args(space, keywords, keywords_w, w_kwds, kwds_mapping,
                           keyword_names_w, _):
     limit = len(keywords)
     if keyword_names_w is not None:
         limit -= len(keyword_names_w)
     for i in range(len(keywords)):
-        if not used_keywords[i]:
+        # again a dangerous-looking loop that either the JIT unrolls
+        # or that is not too bad, because len(kwds_mapping) is small
+        for j in kwds_mapping:
+            if i == j:
+                break
+        else:
             if i < limit:
                 w_key = space.wrap(keywords[i])
             else:
@@ -701,13 +714,13 @@
 
 class ArgErrUnknownKwds(ArgErr):
 
-    def __init__(self, space, num_remainingkwds, keywords, used_keywords,
+    def __init__(self, space, num_remainingkwds, keywords, kwds_mapping,
                  keyword_names_w):
         name = ''
         self.num_kwds = num_remainingkwds
         if num_remainingkwds == 1:
             for i in range(len(keywords)):
-                if not used_keywords[i]:
+                if i not in kwds_mapping:
                     name = keywords[i]
                     if name is None:
                         # We'll assume it's unicode. Encode it.
diff --git a/pypy/interpreter/test/test_argument.py b/pypy/interpreter/test/test_argument.py
--- a/pypy/interpreter/test/test_argument.py
+++ b/pypy/interpreter/test/test_argument.py
@@ -632,11 +632,14 @@
 
     def test_unknown_keywords(self):
         space = DummySpace()
-        err = ArgErrUnknownKwds(space, 1, ['a', 'b'], [True, False], None)
+        err = ArgErrUnknownKwds(space, 1, ['a', 'b'], [0], None)
         s = err.getmsg()
         assert s == "got an unexpected keyword argument 'b'"
+        err = ArgErrUnknownKwds(space, 1, ['a', 'b'], [1], None)
+        s = err.getmsg()
+        assert s == "got an unexpected keyword argument 'a'"
         err = ArgErrUnknownKwds(space, 2, ['a', 'b', 'c'],
-                                [True, False, False], None)
+                                [0], None)
         s = err.getmsg()
         assert s == "got 2 unexpected keyword arguments"
 
@@ -646,7 +649,7 @@
                 defaultencoding = 'utf-8'
         space = DummySpaceUnicode()
         err = ArgErrUnknownKwds(space, 1, ['a', None, 'b', 'c'],
-                                [True, False, True, True],
+                                [0, 3, 2],
                                 [unichr(0x1234), u'b', u'c'])
         s = err.getmsg()
         assert s == "got an unexpected keyword argument '\xe1\x88\xb4'"


More information about the pypy-commit mailing list