Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7e30c9b

Browse files
committed
Add lookup_name() to optimize use of stack frames
The use of com_node() introduces a lot of extra stack frames, enough to cause a stack overflow compiling test.test_parser with the standard interpreter recursionlimit. The com_node() is a convenience function that hides the dispatch details, but comes at a very high cost. It is more efficient to dispatch directly in the callers. In these cases, use lookup_node() and call the dispatched node directly. Also handle yield_stmt in a way that will work with Python 2.1 (suggested by Shane Hathaway)
1 parent 058a5ad commit 7e30c9b

2 files changed

Lines changed: 42 additions & 10 deletions

File tree

Lib/compiler/transformer.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ def argument(self, nodelist):
274274

275275
def expr_stmt(self, nodelist):
276276
# augassign testlist | testlist ('=' testlist)*
277-
exprNode = self.com_node(nodelist[-1])
277+
en = nodelist[-1]
278+
exprNode = self.lookup_node(en)(en[1:])
278279
if len(nodelist) == 1:
279280
n = Discard(exprNode)
280281
n.lineno = exprNode.lineno
@@ -696,6 +697,17 @@ def atom_name(self, nodelist):
696697
# INTERNAL PARSING UTILITIES
697698
#
698699

700+
# The use of com_node() introduces a lot of extra stack frames,
701+
# enough to cause a stack overflow compiling test.test_parser with
702+
# the standard interpreter recursionlimit. The com_node() is a
703+
# convenience function that hides the dispatch details, but comes
704+
# at a very high cost. It is more efficient to dispatch directly
705+
# in the callers. In these cases, use lookup_node() and call the
706+
# dispatched node directly.
707+
708+
def lookup_node(self, node):
709+
return self._dispatch[node[0]]
710+
699711
def com_node(self, node):
700712
# Note: compile.c has handling in com_node for del_stmt, pass_stmt,
701713
# break_stmt, stmt, small_stmt, flow_stmt, simple_stmt,
@@ -938,14 +950,16 @@ def com_binary(self, constructor, nodelist):
938950
"Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])."
939951
l = len(nodelist)
940952
if l == 1:
941-
return self.com_node(nodelist[0])
953+
n = nodelist[0]
954+
return self.lookup_node(n)(n[1:])
942955
items = []
943956
for i in range(0, l, 2):
944-
items.append(self.com_node(nodelist[i]))
957+
n = nodelist[i]
958+
items.append(self.lookup_node(n)(n[1:]))
945959
return constructor(items)
946960

947961
def com_stmt(self, node):
948-
result = self.com_node(node)
962+
result = self.lookup_node(node)(node[1:])
949963
assert result is not None
950964
if isinstance(result, Stmt):
951965
return result
@@ -1254,7 +1268,6 @@ def get_docstring(self, node, n=None):
12541268
symbol.continue_stmt,
12551269
symbol.return_stmt,
12561270
symbol.raise_stmt,
1257-
symbol.yield_stmt,
12581271
symbol.import_stmt,
12591272
symbol.global_stmt,
12601273
symbol.exec_stmt,
@@ -1281,6 +1294,9 @@ def get_docstring(self, node, n=None):
12811294
symbol.atom,
12821295
]
12831296

1297+
if hasattr(symbol, 'yield_stmt'):
1298+
_legal_node_types.append(symbol.yield_stmt)
1299+
12841300
_assign_types = [
12851301
symbol.test,
12861302
symbol.and_test,

Tools/compiler/compiler/transformer.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,8 @@ def argument(self, nodelist):
274274

275275
def expr_stmt(self, nodelist):
276276
# augassign testlist | testlist ('=' testlist)*
277-
exprNode = self.com_node(nodelist[-1])
277+
en = nodelist[-1]
278+
exprNode = self.lookup_node(en)(en[1:])
278279
if len(nodelist) == 1:
279280
n = Discard(exprNode)
280281
n.lineno = exprNode.lineno
@@ -696,6 +697,17 @@ def atom_name(self, nodelist):
696697
# INTERNAL PARSING UTILITIES
697698
#
698699

700+
# The use of com_node() introduces a lot of extra stack frames,
701+
# enough to cause a stack overflow compiling test.test_parser with
702+
# the standard interpreter recursionlimit. The com_node() is a
703+
# convenience function that hides the dispatch details, but comes
704+
# at a very high cost. It is more efficient to dispatch directly
705+
# in the callers. In these cases, use lookup_node() and call the
706+
# dispatched node directly.
707+
708+
def lookup_node(self, node):
709+
return self._dispatch[node[0]]
710+
699711
def com_node(self, node):
700712
# Note: compile.c has handling in com_node for del_stmt, pass_stmt,
701713
# break_stmt, stmt, small_stmt, flow_stmt, simple_stmt,
@@ -938,14 +950,16 @@ def com_binary(self, constructor, nodelist):
938950
"Compile 'NODE (OP NODE)*' into (type, [ node1, ..., nodeN ])."
939951
l = len(nodelist)
940952
if l == 1:
941-
return self.com_node(nodelist[0])
953+
n = nodelist[0]
954+
return self.lookup_node(n)(n[1:])
942955
items = []
943956
for i in range(0, l, 2):
944-
items.append(self.com_node(nodelist[i]))
957+
n = nodelist[i]
958+
items.append(self.lookup_node(n)(n[1:]))
945959
return constructor(items)
946960

947961
def com_stmt(self, node):
948-
result = self.com_node(node)
962+
result = self.lookup_node(node)(node[1:])
949963
assert result is not None
950964
if isinstance(result, Stmt):
951965
return result
@@ -1254,7 +1268,6 @@ def get_docstring(self, node, n=None):
12541268
symbol.continue_stmt,
12551269
symbol.return_stmt,
12561270
symbol.raise_stmt,
1257-
symbol.yield_stmt,
12581271
symbol.import_stmt,
12591272
symbol.global_stmt,
12601273
symbol.exec_stmt,
@@ -1281,6 +1294,9 @@ def get_docstring(self, node, n=None):
12811294
symbol.atom,
12821295
]
12831296

1297+
if hasattr(symbol, 'yield_stmt'):
1298+
_legal_node_types.append(symbol.yield_stmt)
1299+
12841300
_assign_types = [
12851301
symbol.test,
12861302
symbol.and_test,

0 commit comments

Comments
 (0)