%PDF- %PDF-
Direktori : /lib/python3/dist-packages/pythran/analyses/ |
Current File : //lib/python3/dist-packages/pythran/analyses/global_effects.py |
""" GlobalEffects computes function effect on global state. """ from pythran.analyses.aliases import Aliases from pythran.analyses.global_declarations import GlobalDeclarations from pythran.passmanager import ModuleAnalysis from pythran.tables import MODULES from pythran.graph import DiGraph import pythran.intrinsic as intrinsic import gast as ast from functools import reduce class GlobalEffects(ModuleAnalysis): """Add a flag on each function that updates a global variable.""" class FunctionEffect(object): def __init__(self, node): self.func = node if isinstance(node, ast.FunctionDef): self.global_effect = False elif isinstance(node, intrinsic.Intrinsic): self.global_effect = node.global_effects elif isinstance(node, ast.alias): self.global_effect = False elif isinstance(node, str): self.global_effect = False elif isinstance(node, intrinsic.Class): self.global_effect = False elif isinstance(node, intrinsic.UnboundValueType): self.global_effect = True # conservative choice else: print(type(node), node) raise NotImplementedError def __init__(self): self.result = DiGraph() self.node_to_functioneffect = dict() super(GlobalEffects, self).__init__(Aliases, GlobalDeclarations) def prepare(self, node): """ Initialise globals effects as this analyse is inter-procedural. Initialisation done for Pythonic functions and default value set for user defined functions. """ super(GlobalEffects, self).prepare(node) def register_node(module): """ Recursively save globals effect for all functions. """ for v in module.values(): if isinstance(v, dict): # Submodule case register_node(v) else: fe = GlobalEffects.FunctionEffect(v) self.node_to_functioneffect[v] = fe self.result.add_node(fe) if isinstance(v, intrinsic.Class): register_node(v.fields) register_node(self.global_declarations) for module in MODULES.values(): register_node(module) self.node_to_functioneffect[intrinsic.UnboundValue] = \ GlobalEffects.FunctionEffect(intrinsic.UnboundValue) def run(self, node): result = super(GlobalEffects, self).run(node) keep_going = True while keep_going: keep_going = False for function in result: if function.global_effect: for pred in self.result.predecessors(function): if not pred.global_effect: keep_going = pred.global_effect = True self.result = {f.func for f in result if f.global_effect} return self.result def visit_FunctionDef(self, node): self.current_function = self.node_to_functioneffect[node] assert self.current_function in self.result self.generic_visit(node) def visit_Print(self, _): self.current_function.global_effect = True def visit_Call(self, node): # try to get all aliases of the function, if possible # else use [] as a fallback func_aliases = self.aliases[node.func] # expand argument if any func_aliases = reduce( # all funcs lambda x, y: x + (list(self.node_to_functioneffect.keys()) if isinstance(y, ast.Name) else [y]), func_aliases, list()) for func_alias in func_aliases: # special hook for bound functions if isinstance(func_alias, ast.Call): fake_call = ast.Call(func_alias.args[0], func_alias.args[1:], []) self.visit(fake_call) continue # conservative choice if func_alias not in self.node_to_functioneffect: func_alias = intrinsic.UnboundValue func_alias = self.node_to_functioneffect[func_alias] self.result.add_edge(self.current_function, func_alias) self.generic_visit(node)