Skip to content

construct 'lnotab' byte string for code objects #93

Open
wants to merge 1 commit into from
View
16 Cython/Compiler/ExprNodes.py
@@ -8035,10 +8035,13 @@ 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
result_code = None
+ lno_tab = None
def __init__(self, def_node):
ExprNode.__init__(self, def_node.pos, def_node=def_node)
@@ -8064,6 +8067,12 @@ def generate_result_code(self, code):
if self.result_code is None:
self.result_code = code.get_py_const(py_object_type, 'codeobj', cleanup_level=2)
+ if self.lno_tab:
+ self.lno_tab.generate_evaluation_code(code)
+ lno_tab = self.lno_tab.result()
+ else:
+ lno_tab = Naming.empty_bytes
+
code = code.get_cached_constants_writer()
code.mark_pos(self.pos)
func = self.def_node
@@ -8094,10 +8103,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
@@ -19,7 +19,7 @@
from .Visitor import CythonTransform, EnvTransform, ScopeTrackingTransform
from .UtilNodes import LetNode, LetRefNode, ResultRefNode
from .TreeFragment import TreeFragment
-from .StringEncoding import EncodedString
+from .StringEncoding import EncodedString, BytesLiteral, _unicode
from .Errors import error, warning, CompileError, InternalError
from .Code import UtilityCode
@@ -2418,6 +2418,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
@@ -137,7 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from .ParseTreeTransforms import CalculateQualifiedNamesTransform
from .TypeInference import MarkParallelAssignments, 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
@@ -207,6 +207,7 @@ def create_pipeline(context, mode, exclude_classes=()):
FinalOptimizePhase(context),
GilCheck(),
UseUtilityCodeDefinitions(context),
+ CreateLineNumberMaps(context),
]
filtered_stages = []
for s in stages:
Something went wrong with that request. Please try again.