| Index: third_party/cython/src/Cython/Compiler/Pipeline.py
|
| diff --git a/third_party/cython/src/Cython/Compiler/Pipeline.py b/third_party/cython/src/Cython/Compiler/Pipeline.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1b6eab1e70e5a44330ca73f1950a94ee8dc7391f
|
| --- /dev/null
|
| +++ b/third_party/cython/src/Cython/Compiler/Pipeline.py
|
| @@ -0,0 +1,342 @@
|
| +import itertools
|
| +from time import time
|
| +
|
| +import Errors
|
| +import DebugFlags
|
| +import Options
|
| +from Visitor import CythonTransform
|
| +from Errors import CompileError, InternalError, AbortError
|
| +import Naming
|
| +
|
| +#
|
| +# Really small pipeline stages
|
| +#
|
| +def dumptree(t):
|
| + # For quick debugging in pipelines
|
| + print t.dump()
|
| + return t
|
| +
|
| +def abort_on_errors(node):
|
| + # Stop the pipeline if there are any errors.
|
| + if Errors.num_errors != 0:
|
| + raise AbortError("pipeline break")
|
| + return node
|
| +
|
| +def parse_stage_factory(context):
|
| + def parse(compsrc):
|
| + source_desc = compsrc.source_desc
|
| + full_module_name = compsrc.full_module_name
|
| + initial_pos = (source_desc, 1, 0)
|
| + saved_cimport_from_pyx, Options.cimport_from_pyx = Options.cimport_from_pyx, False
|
| + scope = context.find_module(full_module_name, pos = initial_pos, need_pxd = 0,
|
| + check_module_name = not Options.embed)
|
| + Options.cimport_from_pyx = saved_cimport_from_pyx
|
| + tree = context.parse(source_desc, scope, pxd = 0, full_module_name = full_module_name)
|
| + tree.compilation_source = compsrc
|
| + tree.scope = scope
|
| + tree.is_pxd = False
|
| + return tree
|
| + return parse
|
| +
|
| +def parse_pxd_stage_factory(context, scope, module_name):
|
| + def parse(source_desc):
|
| + tree = context.parse(source_desc, scope, pxd=True,
|
| + full_module_name=module_name)
|
| + tree.scope = scope
|
| + tree.is_pxd = True
|
| + return tree
|
| + return parse
|
| +
|
| +def generate_pyx_code_stage_factory(options, result):
|
| + def generate_pyx_code_stage(module_node):
|
| + module_node.process_implementation(options, result)
|
| + result.compilation_source = module_node.compilation_source
|
| + return result
|
| + return generate_pyx_code_stage
|
| +
|
| +def inject_pxd_code_stage_factory(context):
|
| + def inject_pxd_code_stage(module_node):
|
| + from textwrap import dedent
|
| + stats = module_node.body.stats
|
| + for name, (statlistnode, scope) in context.pxds.iteritems():
|
| + module_node.merge_in(statlistnode, scope)
|
| + return module_node
|
| + return inject_pxd_code_stage
|
| +
|
| +def use_utility_code_definitions(scope, target, seen=None):
|
| + if seen is None:
|
| + seen = set()
|
| +
|
| + for entry in scope.entries.itervalues():
|
| + if entry in seen:
|
| + continue
|
| +
|
| + seen.add(entry)
|
| + if entry.used and entry.utility_code_definition:
|
| + target.use_utility_code(entry.utility_code_definition)
|
| + for required_utility in entry.utility_code_definition.requires:
|
| + target.use_utility_code(required_utility)
|
| + elif entry.as_module:
|
| + use_utility_code_definitions(entry.as_module, target, seen)
|
| +
|
| +def inject_utility_code_stage_factory(context):
|
| + def inject_utility_code_stage(module_node):
|
| + use_utility_code_definitions(context.cython_scope, module_node.scope)
|
| + added = []
|
| + # Note: the list might be extended inside the loop (if some utility code
|
| + # pulls in other utility code, explicitly or implicitly)
|
| + for utilcode in module_node.scope.utility_code_list:
|
| + if utilcode in added: continue
|
| + added.append(utilcode)
|
| + if utilcode.requires:
|
| + for dep in utilcode.requires:
|
| + if not dep in added and not dep in module_node.scope.utility_code_list:
|
| + module_node.scope.utility_code_list.append(dep)
|
| + tree = utilcode.get_tree()
|
| + if tree:
|
| + module_node.merge_in(tree.body, tree.scope, merge_scope=True)
|
| + return module_node
|
| + return inject_utility_code_stage
|
| +
|
| +class UseUtilityCodeDefinitions(CythonTransform):
|
| + # Temporary hack to use any utility code in nodes' "utility_code_definitions".
|
| + # This should be moved to the code generation phase of the relevant nodes once
|
| + # it is safe to generate CythonUtilityCode at code generation time.
|
| + def __call__(self, node):
|
| + self.scope = node.scope
|
| + return super(UseUtilityCodeDefinitions, self).__call__(node)
|
| +
|
| + def process_entry(self, entry):
|
| + if entry:
|
| + for utility_code in (entry.utility_code, entry.utility_code_definition):
|
| + if utility_code:
|
| + self.scope.use_utility_code(utility_code)
|
| +
|
| + def visit_AttributeNode(self, node):
|
| + self.process_entry(node.entry)
|
| + return node
|
| +
|
| + def visit_NameNode(self, node):
|
| + self.process_entry(node.entry)
|
| + self.process_entry(node.type_entry)
|
| + return node
|
| +
|
| +#
|
| +# Pipeline factories
|
| +#
|
| +
|
| +def create_pipeline(context, mode, exclude_classes=()):
|
| + assert mode in ('pyx', 'py', 'pxd')
|
| + from Visitor import PrintTree
|
| + from ParseTreeTransforms import WithTransform, NormalizeTree, PostParse, PxdPostParse
|
| + from ParseTreeTransforms import ForwardDeclareTypes, AnalyseDeclarationsTransform
|
| + from ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes
|
| + from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform
|
| + from ParseTreeTransforms import InterpretCompilerDirectives, TransformBuiltinMethods
|
| + from ParseTreeTransforms import ExpandInplaceOperators, ParallelRangeTransform
|
| + from ParseTreeTransforms import CalculateQualifiedNamesTransform
|
| + from TypeInference import MarkParallelAssignments, MarkOverflowingArithmetic
|
| + from ParseTreeTransforms import AdjustDefByDirectives, AlignFunctionDefinitions
|
| + from ParseTreeTransforms import RemoveUnreachableCode, GilCheck
|
| + from FlowControl import ControlFlowAnalysis
|
| + from AnalysedTreeTransforms import AutoTestDictTransform
|
| + from AutoDocTransforms import EmbedSignature
|
| + from Optimize import FlattenInListTransform, SwitchTransform, IterationTransform
|
| + from Optimize import EarlyReplaceBuiltinCalls, OptimizeBuiltinCalls
|
| + from Optimize import InlineDefNodeCalls
|
| + from Optimize import ConstantFolding, FinalOptimizePhase
|
| + from Optimize import DropRefcountingTransform
|
| + from Optimize import ConsolidateOverflowCheck
|
| + from Buffer import IntroduceBufferAuxiliaryVars
|
| + from ModuleNode import check_c_declarations, check_c_declarations_pxd
|
| +
|
| +
|
| + if mode == 'pxd':
|
| + _check_c_declarations = check_c_declarations_pxd
|
| + _specific_post_parse = PxdPostParse(context)
|
| + else:
|
| + _check_c_declarations = check_c_declarations
|
| + _specific_post_parse = None
|
| +
|
| + if mode == 'py':
|
| + _align_function_definitions = AlignFunctionDefinitions(context)
|
| + else:
|
| + _align_function_definitions = None
|
| +
|
| + # NOTE: This is the "common" parts of the pipeline, which is also
|
| + # code in pxd files. So it will be run multiple times in a
|
| + # compilation stage.
|
| + stages = [
|
| + NormalizeTree(context),
|
| + PostParse(context),
|
| + _specific_post_parse,
|
| + InterpretCompilerDirectives(context, context.compiler_directives),
|
| + ParallelRangeTransform(context),
|
| + AdjustDefByDirectives(context),
|
| + MarkClosureVisitor(context),
|
| + _align_function_definitions,
|
| + RemoveUnreachableCode(context),
|
| + ConstantFolding(),
|
| + FlattenInListTransform(),
|
| + WithTransform(context),
|
| + DecoratorTransform(context),
|
| + ForwardDeclareTypes(context),
|
| + AnalyseDeclarationsTransform(context),
|
| + AutoTestDictTransform(context),
|
| + EmbedSignature(context),
|
| + EarlyReplaceBuiltinCalls(context), ## Necessary?
|
| + TransformBuiltinMethods(context), ## Necessary?
|
| + MarkParallelAssignments(context),
|
| + ControlFlowAnalysis(context),
|
| + RemoveUnreachableCode(context),
|
| + # MarkParallelAssignments(context),
|
| + MarkOverflowingArithmetic(context),
|
| + IntroduceBufferAuxiliaryVars(context),
|
| + _check_c_declarations,
|
| + InlineDefNodeCalls(context),
|
| + AnalyseExpressionsTransform(context),
|
| + FindInvalidUseOfFusedTypes(context),
|
| + ExpandInplaceOperators(context),
|
| + OptimizeBuiltinCalls(context), ## Necessary?
|
| + CreateClosureClasses(context), ## After all lookups and type inference
|
| + CalculateQualifiedNamesTransform(context),
|
| + ConsolidateOverflowCheck(context),
|
| + IterationTransform(context),
|
| + SwitchTransform(),
|
| + DropRefcountingTransform(),
|
| + FinalOptimizePhase(context),
|
| + GilCheck(),
|
| + UseUtilityCodeDefinitions(context),
|
| + ]
|
| + filtered_stages = []
|
| + for s in stages:
|
| + if s.__class__ not in exclude_classes:
|
| + filtered_stages.append(s)
|
| + return filtered_stages
|
| +
|
| +def create_pyx_pipeline(context, options, result, py=False, exclude_classes=()):
|
| + if py:
|
| + mode = 'py'
|
| + else:
|
| + mode = 'pyx'
|
| + test_support = []
|
| + if options.evaluate_tree_assertions:
|
| + from Cython.TestUtils import TreeAssertVisitor
|
| + test_support.append(TreeAssertVisitor())
|
| +
|
| + if options.gdb_debug:
|
| + from Cython.Debugger import DebugWriter # requires Py2.5+
|
| + from ParseTreeTransforms import DebugTransform
|
| + context.gdb_debug_outputwriter = DebugWriter.CythonDebugWriter(
|
| + options.output_dir)
|
| + debug_transform = [DebugTransform(context, options, result)]
|
| + else:
|
| + debug_transform = []
|
| +
|
| + return list(itertools.chain(
|
| + [parse_stage_factory(context)],
|
| + create_pipeline(context, mode, exclude_classes=exclude_classes),
|
| + test_support,
|
| + [inject_pxd_code_stage_factory(context),
|
| + inject_utility_code_stage_factory(context),
|
| + abort_on_errors],
|
| + debug_transform,
|
| + [generate_pyx_code_stage_factory(options, result)]))
|
| +
|
| +def create_pxd_pipeline(context, scope, module_name):
|
| + from CodeGeneration import ExtractPxdCode
|
| +
|
| + # The pxd pipeline ends up with a CCodeWriter containing the
|
| + # code of the pxd, as well as a pxd scope.
|
| + return [
|
| + parse_pxd_stage_factory(context, scope, module_name)
|
| + ] + create_pipeline(context, 'pxd') + [
|
| + ExtractPxdCode()
|
| + ]
|
| +
|
| +def create_py_pipeline(context, options, result):
|
| + return create_pyx_pipeline(context, options, result, py=True)
|
| +
|
| +def create_pyx_as_pxd_pipeline(context, result):
|
| + from ParseTreeTransforms import AlignFunctionDefinitions, \
|
| + MarkClosureVisitor, WithTransform, AnalyseDeclarationsTransform
|
| + from Optimize import ConstantFolding, FlattenInListTransform
|
| + from Nodes import StatListNode
|
| + pipeline = []
|
| + pyx_pipeline = create_pyx_pipeline(context, context.options, result,
|
| + exclude_classes=[
|
| + AlignFunctionDefinitions,
|
| + MarkClosureVisitor,
|
| + ConstantFolding,
|
| + FlattenInListTransform,
|
| + WithTransform
|
| + ])
|
| + for stage in pyx_pipeline:
|
| + pipeline.append(stage)
|
| + if isinstance(stage, AnalyseDeclarationsTransform):
|
| + # This is the last stage we need.
|
| + break
|
| + def fake_pxd(root):
|
| + for entry in root.scope.entries.values():
|
| + if not entry.in_cinclude:
|
| + entry.defined_in_pxd = 1
|
| + if entry.name == entry.cname and entry.visibility != 'extern':
|
| + # Always mangle non-extern cimported entries.
|
| + entry.cname = entry.scope.mangle(Naming.func_prefix, entry.name)
|
| + return StatListNode(root.pos, stats=[]), root.scope
|
| + pipeline.append(fake_pxd)
|
| + return pipeline
|
| +
|
| +def insert_into_pipeline(pipeline, transform, before=None, after=None):
|
| + """
|
| + Insert a new transform into the pipeline after or before an instance of
|
| + the given class. e.g.
|
| +
|
| + pipeline = insert_into_pipeline(pipeline, transform,
|
| + after=AnalyseDeclarationsTransform)
|
| + """
|
| + assert before or after
|
| +
|
| + cls = before or after
|
| + for i, t in enumerate(pipeline):
|
| + if isinstance(t, cls):
|
| + break
|
| +
|
| + if after:
|
| + i += 1
|
| +
|
| + return pipeline[:i] + [transform] + pipeline[i:]
|
| +
|
| +#
|
| +# Running a pipeline
|
| +#
|
| +
|
| +def run_pipeline(pipeline, source, printtree=True):
|
| + from Cython.Compiler.Visitor import PrintTree
|
| +
|
| + error = None
|
| + data = source
|
| + try:
|
| + try:
|
| + for phase in pipeline:
|
| + if phase is not None:
|
| + if DebugFlags.debug_verbose_pipeline:
|
| + t = time()
|
| + print "Entering pipeline phase %r" % phase
|
| + if not printtree and isinstance(phase, PrintTree):
|
| + continue
|
| + data = phase(data)
|
| + if DebugFlags.debug_verbose_pipeline:
|
| + print " %.3f seconds" % (time() - t)
|
| + except CompileError, err:
|
| + # err is set
|
| + Errors.report_error(err)
|
| + error = err
|
| + except InternalError, err:
|
| + # Only raise if there was not an earlier error
|
| + if Errors.num_errors == 0:
|
| + raise
|
| + error = err
|
| + except AbortError, err:
|
| + error = err
|
| + return (error, data)
|
|
|