|
|
from __future__ import absolute_import |
|
|
|
|
|
import itertools |
|
|
from time import time |
|
|
|
|
|
from . import Errors |
|
|
from . import DebugFlags |
|
|
from . import Options |
|
|
from .Errors import CompileError, InternalError, AbortError |
|
|
from . import Naming |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def dumptree(t): |
|
|
|
|
|
print(t.dump()) |
|
|
return t |
|
|
|
|
|
def abort_on_errors(node): |
|
|
|
|
|
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) |
|
|
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): |
|
|
for name, (statlistnode, scope) in context.pxds.items(): |
|
|
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.values(): |
|
|
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 sort_utility_codes(utilcodes): |
|
|
ranks = {} |
|
|
def get_rank(utilcode): |
|
|
if utilcode not in ranks: |
|
|
ranks[utilcode] = 0 |
|
|
original_order = len(ranks) |
|
|
ranks[utilcode] = 1 + min([get_rank(dep) for dep in utilcode.requires or ()] or [-1]) + original_order * 1e-8 |
|
|
return ranks[utilcode] |
|
|
for utilcode in utilcodes: |
|
|
get_rank(utilcode) |
|
|
return [utilcode for utilcode, _ in sorted(ranks.items(), key=lambda kv: kv[1])] |
|
|
|
|
|
|
|
|
def normalize_deps(utilcodes): |
|
|
deps = {} |
|
|
for utilcode in utilcodes: |
|
|
deps[utilcode] = utilcode |
|
|
|
|
|
def unify_dep(dep): |
|
|
if dep in deps: |
|
|
return deps[dep] |
|
|
else: |
|
|
deps[dep] = dep |
|
|
return dep |
|
|
|
|
|
for utilcode in utilcodes: |
|
|
utilcode.requires = [unify_dep(dep) for dep in utilcode.requires or ()] |
|
|
|
|
|
|
|
|
def inject_utility_code_stage_factory(context): |
|
|
def inject_utility_code_stage(module_node): |
|
|
module_node.prepare_utility_code() |
|
|
use_utility_code_definitions(context.cython_scope, module_node.scope) |
|
|
module_node.scope.utility_code_list = sort_utility_codes(module_node.scope.utility_code_list) |
|
|
normalize_deps(module_node.scope.utility_code_list) |
|
|
added = [] |
|
|
|
|
|
|
|
|
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 dep not in added and dep not in module_node.scope.utility_code_list: |
|
|
module_node.scope.utility_code_list.append(dep) |
|
|
tree = utilcode.get_tree(cython_scope=context.cython_scope) |
|
|
if tree: |
|
|
module_node.merge_in(tree.body, tree.scope, merge_scope=True) |
|
|
return module_node |
|
|
return inject_utility_code_stage |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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, InjectGilHandling, AnalyseDeclarationsTransform |
|
|
from .ParseTreeTransforms import AnalyseExpressionsTransform, FindInvalidUseOfFusedTypes |
|
|
from .ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform |
|
|
from .ParseTreeTransforms import TrackNumpyAttributes, 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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stages = [ |
|
|
NormalizeTree(context), |
|
|
PostParse(context), |
|
|
_specific_post_parse, |
|
|
TrackNumpyAttributes(), |
|
|
InterpretCompilerDirectives(context, context.compiler_directives), |
|
|
ParallelRangeTransform(context), |
|
|
AdjustDefByDirectives(context), |
|
|
WithTransform(context), |
|
|
MarkClosureVisitor(context), |
|
|
_align_function_definitions, |
|
|
RemoveUnreachableCode(context), |
|
|
ConstantFolding(), |
|
|
FlattenInListTransform(), |
|
|
DecoratorTransform(context), |
|
|
ForwardDeclareTypes(context), |
|
|
InjectGilHandling(), |
|
|
AnalyseDeclarationsTransform(context), |
|
|
AutoTestDictTransform(context), |
|
|
EmbedSignature(context), |
|
|
EarlyReplaceBuiltinCalls(context), |
|
|
TransformBuiltinMethods(context), |
|
|
MarkParallelAssignments(context), |
|
|
ControlFlowAnalysis(context), |
|
|
RemoveUnreachableCode(context), |
|
|
|
|
|
MarkOverflowingArithmetic(context), |
|
|
IntroduceBufferAuxiliaryVars(context), |
|
|
_check_c_declarations, |
|
|
InlineDefNodeCalls(context), |
|
|
AnalyseExpressionsTransform(context), |
|
|
FindInvalidUseOfFusedTypes(context), |
|
|
ExpandInplaceOperators(context), |
|
|
IterationTransform(context), |
|
|
SwitchTransform(context), |
|
|
OptimizeBuiltinCalls(context), |
|
|
CreateClosureClasses(context), |
|
|
CalculateQualifiedNamesTransform(context), |
|
|
ConsolidateOverflowCheck(context), |
|
|
DropRefcountingTransform(), |
|
|
FinalOptimizePhase(context), |
|
|
GilCheck(), |
|
|
] |
|
|
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 ..TestUtils import TreeAssertVisitor |
|
|
test_support.append(TreeAssertVisitor()) |
|
|
|
|
|
if options.gdb_debug: |
|
|
from ..Debugger import DebugWriter |
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
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): |
|
|
|
|
|
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': |
|
|
|
|
|
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:] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_pipeline_entry_points = {} |
|
|
|
|
|
|
|
|
def run_pipeline(pipeline, source, printtree=True): |
|
|
from .Visitor import PrintTree |
|
|
exec_ns = globals().copy() if DebugFlags.debug_verbose_pipeline else None |
|
|
|
|
|
def run(phase, data): |
|
|
return phase(data) |
|
|
|
|
|
error = None |
|
|
data = source |
|
|
try: |
|
|
try: |
|
|
for phase in pipeline: |
|
|
if phase is not None: |
|
|
if not printtree and isinstance(phase, PrintTree): |
|
|
continue |
|
|
if DebugFlags.debug_verbose_pipeline: |
|
|
t = time() |
|
|
print("Entering pipeline phase %r" % phase) |
|
|
|
|
|
phase_name = getattr(phase, '__name__', type(phase).__name__) |
|
|
try: |
|
|
run = _pipeline_entry_points[phase_name] |
|
|
except KeyError: |
|
|
exec("def %s(phase, data): return phase(data)" % phase_name, exec_ns) |
|
|
run = _pipeline_entry_points[phase_name] = exec_ns[phase_name] |
|
|
data = run(phase, data) |
|
|
if DebugFlags.debug_verbose_pipeline: |
|
|
print(" %.3f seconds" % (time() - t)) |
|
|
except CompileError as err: |
|
|
|
|
|
Errors.report_error(err, use_stack=False) |
|
|
error = err |
|
|
except InternalError as err: |
|
|
|
|
|
if Errors.num_errors == 0: |
|
|
raise |
|
|
error = err |
|
|
except AbortError as err: |
|
|
error = err |
|
|
return (error, data) |
|
|
|