| from typing import List, Set, Tuple |
| |
| from mypyc.ir.ops import ( |
| OpVisitor, Register, Goto, Assign, AssignMulti, SetMem, Call, MethodCall, LoadErrorValue, |
| LoadLiteral, GetAttr, SetAttr, LoadStatic, InitStatic, TupleGet, TupleSet, Box, Unbox, |
| Cast, RaiseStandardError, CallC, Truncate, LoadGlobal, IntOp, ComparisonOp, LoadMem, |
| GetElementPtr, LoadAddress, KeepAlive, Branch, Return, Unreachable, RegisterOp, BasicBlock |
| ) |
| from mypyc.ir.rtypes import RInstance |
| from mypyc.analysis.dataflow import MAYBE_ANALYSIS, run_analysis, AnalysisResult, CFG |
| |
| GenAndKill = Tuple[Set[None], Set[None]] |
| |
| CLEAN: GenAndKill = (set(), set()) |
| DIRTY: GenAndKill = ({None}, {None}) |
| |
| |
| class SelfLeakedVisitor(OpVisitor[GenAndKill]): |
| """Analyze whether 'self' may be seen by arbitrary code in '__init__'. |
| |
| More formally, the set is not empty if along some path from IR entry point |
| arbitrary code could have been executed that has access to 'self'. |
| |
| (We don't consider access via 'gc.get_objects()'.) |
| """ |
| |
| def __init__(self, self_reg: Register) -> None: |
| self.self_reg = self_reg |
| |
| def visit_goto(self, op: Goto) -> GenAndKill: |
| return CLEAN |
| |
| def visit_branch(self, op: Branch) -> GenAndKill: |
| return CLEAN |
| |
| def visit_return(self, op: Return) -> GenAndKill: |
| # Consider all exits from the function 'dirty' since they implicitly |
| # cause 'self' to be returned. |
| return DIRTY |
| |
| def visit_unreachable(self, op: Unreachable) -> GenAndKill: |
| return CLEAN |
| |
| def visit_assign(self, op: Assign) -> GenAndKill: |
| if op.src is self.self_reg or op.dest is self.self_reg: |
| return DIRTY |
| return CLEAN |
| |
| def visit_assign_multi(self, op: AssignMulti) -> GenAndKill: |
| return CLEAN |
| |
| def visit_set_mem(self, op: SetMem) -> GenAndKill: |
| return CLEAN |
| |
| def visit_call(self, op: Call) -> GenAndKill: |
| fn = op.fn |
| if fn.class_name and fn.name == '__init__': |
| self_type = op.fn.sig.args[0].type |
| assert isinstance(self_type, RInstance) |
| cl = self_type.class_ir |
| if not cl.init_self_leak: |
| return CLEAN |
| return self.check_register_op(op) |
| |
| def visit_method_call(self, op: MethodCall) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_load_error_value(self, op: LoadErrorValue) -> GenAndKill: |
| return CLEAN |
| |
| def visit_load_literal(self, op: LoadLiteral) -> GenAndKill: |
| return CLEAN |
| |
| def visit_get_attr(self, op: GetAttr) -> GenAndKill: |
| cl = op.class_type.class_ir |
| if cl.get_method(op.attr): |
| # Property -- calls a function |
| return self.check_register_op(op) |
| return CLEAN |
| |
| def visit_set_attr(self, op: SetAttr) -> GenAndKill: |
| cl = op.class_type.class_ir |
| if cl.get_method(op.attr): |
| # Property - calls a function |
| return self.check_register_op(op) |
| return CLEAN |
| |
| def visit_load_static(self, op: LoadStatic) -> GenAndKill: |
| return CLEAN |
| |
| def visit_init_static(self, op: InitStatic) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_tuple_get(self, op: TupleGet) -> GenAndKill: |
| return CLEAN |
| |
| def visit_tuple_set(self, op: TupleSet) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_box(self, op: Box) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_unbox(self, op: Unbox) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_cast(self, op: Cast) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill: |
| return CLEAN |
| |
| def visit_call_c(self, op: CallC) -> GenAndKill: |
| return self.check_register_op(op) |
| |
| def visit_truncate(self, op: Truncate) -> GenAndKill: |
| return CLEAN |
| |
| def visit_load_global(self, op: LoadGlobal) -> GenAndKill: |
| return CLEAN |
| |
| def visit_int_op(self, op: IntOp) -> GenAndKill: |
| return CLEAN |
| |
| def visit_comparison_op(self, op: ComparisonOp) -> GenAndKill: |
| return CLEAN |
| |
| def visit_load_mem(self, op: LoadMem) -> GenAndKill: |
| return CLEAN |
| |
| def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill: |
| return CLEAN |
| |
| def visit_load_address(self, op: LoadAddress) -> GenAndKill: |
| return CLEAN |
| |
| def visit_keep_alive(self, op: KeepAlive) -> GenAndKill: |
| return CLEAN |
| |
| def check_register_op(self, op: RegisterOp) -> GenAndKill: |
| if any(src is self.self_reg for src in op.sources()): |
| return DIRTY |
| return CLEAN |
| |
| |
| def analyze_self_leaks(blocks: List[BasicBlock], |
| self_reg: Register, |
| cfg: CFG) -> AnalysisResult[None]: |
| return run_analysis(blocks=blocks, |
| cfg=cfg, |
| gen_and_kill=SelfLeakedVisitor(self_reg), |
| initial=set(), |
| backward=False, |
| kind=MAYBE_ANALYSIS) |