Skip to content
Browse files

construct 'lnotab' byte string for code objects to eventually support…

… runtime mapping of line numbers
  • Loading branch information...
1 parent f69ac18 commit 7596381c69594965d189ae8e4d58c00b3861270a @scoder committed Mar 14, 2012
Showing with 71 additions and 4 deletions.
  1. +14 −2 Cython/Compiler/ExprNodes.py
  2. +55 −1 Cython/Compiler/ParseTreeTransforms.py
  3. +2 −1 Cython/Compiler/Pipeline.py
View
16 Cython/Compiler/ExprNodes.py
@@ -6313,9 +6313,12 @@ class CodeObjectNode(ExprNode):
#
# def_node DefNode the Python function node
# varnames TupleNode a tuple with all local variable names
+ # lno_tab BytesNode a bytes string containing compressed line number offsets
+ # (see CPython's Objects/lnotab_notes.txt)
- subexprs = ['varnames']
+ subexprs = ['varnames', 'lno_tab']
is_temp = False
+ lno_tab = None
def __init__(self, def_node):
ExprNode.__init__(self, def_node.pos, def_node=def_node)
@@ -6337,6 +6340,12 @@ def calculate_result_code(self):
return self.result_code
def generate_result_code(self, code):
+ if self.lno_tab:
+ self.lno_tab.generate_evaluation_code(code)
+ lno_tab = self.lno_tab.result()
+ else:
+ lno_tab = Naming.empty_bytes
+
self.result_code = code.get_py_const(py_object_type, 'codeobj_', cleanup_level=2)
code = code.get_cached_constants_writer()
@@ -6362,10 +6371,13 @@ def generate_result_code(self, code):
file_path_const, # filename
func_name, # name
self.pos[1], # firstlineno
- Naming.empty_bytes, # lnotab
+ lno_tab, # lnotab
code.error_goto_if_null(self.result_code, self.pos),
))
+ if self.lno_tab:
+ self.lno_tab.generate_disposal_code(code)
+
class DefaultLiteralArgNode(ExprNode):
# CyFunction's literal argument default value
View
56 Cython/Compiler/ParseTreeTransforms.py
@@ -15,7 +15,7 @@
from Cython.Compiler.Visitor import CythonTransform, EnvTransform, ScopeTrackingTransform
from Cython.Compiler.UtilNodes import LetNode, LetRefNode, ResultRefNode
from Cython.Compiler.TreeFragment import TreeFragment
-from Cython.Compiler.StringEncoding import EncodedString
+from Cython.Compiler.StringEncoding import EncodedString, BytesLiteral, _unicode
from Cython.Compiler.Errors import error, warning, CompileError, InternalError
import copy
@@ -2223,6 +2223,60 @@ def visit_CFuncDefNode(self, node):
return node
+class CreateLineNumberMaps(EnvTransform):
+ """
+ Create line number maps for code objects as specified
+ by CPython's Objects/lnotab_notes.txt. This is a simplified
+ implementation because we don't have byte code, so we use
+ the line numbers also as byte code offsets, thus making the
+ runtime lookup straight forward as well.
+ """
+ line_numbers = None
+
+ def visit_CodeObjectNode(self, node):
+ outer_context = self.line_numbers
+ def_node = node.def_node
+ self.line_numbers = set()
+ self.visitchildren(def_node)
+ if self.line_numbers:
+ node.lno_tab = self.build_lnotab(def_node.pos, self.line_numbers)
+ self.line_numbers = outer_context
+ return node
+
+ def visit_Node(self, node):
+ """
+ Collect line numbers of all non-nogil nodes within functions
+ (where Python code line numbers are relevant).
+ """
+ if self.line_numbers is not None and not self.current_env().nogil:
+ self.line_numbers.add(node.pos[1])
+ self.visitchildren(node)
+ return node
+
+ def build_lnotab(self, node_pos, line_numbers):
+ line_numbers = sorted(line_numbers)
+
+ chr255 = chr(255)
+ lnotab = []
+ last_line = node_pos[1]
+ for line in line_numbers:
+ offset = line - last_line
+ if offset > 255:
+ lnotab.append(chr255 * ((offset // 255) * 2))
+ offset %= 255
+ if not offset:
+ continue
+ lnotab.append(chr(offset)*2)
+ last_line = line
+
+ lnotab_string = ''.join(lnotab)
+ if isinstance(lnotab_string, _unicode): # Py3
+ lnotab_string = lnotab_string.encode('iso8859-1')
+ string_literal = BytesLiteral(lnotab_string)
+ string_literal.encoding = 'iso8859-1'
+ return ExprNodes.BytesNode(node_pos, value=string_literal)
+
+
class GilCheck(VisitorTransform):
"""
Call `node.gil_check(env)` on each node to make sure we hold the
View
3 Cython/Compiler/Pipeline.py
@@ -131,7 +131,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from ParseTreeTransforms import ExpandInplaceOperators, ParallelRangeTransform
from TypeInference import MarkAssignments, MarkOverflowingArithmetic
from ParseTreeTransforms import AdjustDefByDirectives, AlignFunctionDefinitions
- from ParseTreeTransforms import RemoveUnreachableCode, GilCheck
+ from ParseTreeTransforms import RemoveUnreachableCode, CreateLineNumberMaps, GilCheck
from FlowControl import ControlFlowAnalysis
from AnalysedTreeTransforms import AutoTestDictTransform
from AutoDocTransforms import EmbedSignature
@@ -198,6 +198,7 @@ def create_pipeline(context, mode, exclude_classes=()):
FinalOptimizePhase(context),
GilCheck(),
UseUtilityCodeDefinitions(context),
+ CreateLineNumberMaps(context),
]
filtered_stages = []
for s in stages:

0 comments on commit 7596381

Please sign in to comment.
Something went wrong with that request. Please try again.