selfleaks.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. from __future__ import annotations
  2. from typing import Set, Tuple
  3. from mypyc.analysis.dataflow import CFG, MAYBE_ANALYSIS, AnalysisResult, run_analysis
  4. from mypyc.ir.ops import (
  5. Assign,
  6. AssignMulti,
  7. BasicBlock,
  8. Box,
  9. Branch,
  10. Call,
  11. CallC,
  12. Cast,
  13. ComparisonOp,
  14. Extend,
  15. FloatComparisonOp,
  16. FloatNeg,
  17. FloatOp,
  18. GetAttr,
  19. GetElementPtr,
  20. Goto,
  21. InitStatic,
  22. IntOp,
  23. KeepAlive,
  24. LoadAddress,
  25. LoadErrorValue,
  26. LoadGlobal,
  27. LoadLiteral,
  28. LoadMem,
  29. LoadStatic,
  30. MethodCall,
  31. OpVisitor,
  32. RaiseStandardError,
  33. Register,
  34. RegisterOp,
  35. Return,
  36. SetAttr,
  37. SetMem,
  38. Truncate,
  39. TupleGet,
  40. TupleSet,
  41. Unbox,
  42. Unreachable,
  43. )
  44. from mypyc.ir.rtypes import RInstance
  45. GenAndKill = Tuple[Set[None], Set[None]]
  46. CLEAN: GenAndKill = (set(), set())
  47. DIRTY: GenAndKill = ({None}, {None})
  48. class SelfLeakedVisitor(OpVisitor[GenAndKill]):
  49. """Analyze whether 'self' may be seen by arbitrary code in '__init__'.
  50. More formally, the set is not empty if along some path from IR entry point
  51. arbitrary code could have been executed that has access to 'self'.
  52. (We don't consider access via 'gc.get_objects()'.)
  53. """
  54. def __init__(self, self_reg: Register) -> None:
  55. self.self_reg = self_reg
  56. def visit_goto(self, op: Goto) -> GenAndKill:
  57. return CLEAN
  58. def visit_branch(self, op: Branch) -> GenAndKill:
  59. return CLEAN
  60. def visit_return(self, op: Return) -> GenAndKill:
  61. # Consider all exits from the function 'dirty' since they implicitly
  62. # cause 'self' to be returned.
  63. return DIRTY
  64. def visit_unreachable(self, op: Unreachable) -> GenAndKill:
  65. return CLEAN
  66. def visit_assign(self, op: Assign) -> GenAndKill:
  67. if op.src is self.self_reg or op.dest is self.self_reg:
  68. return DIRTY
  69. return CLEAN
  70. def visit_assign_multi(self, op: AssignMulti) -> GenAndKill:
  71. return CLEAN
  72. def visit_set_mem(self, op: SetMem) -> GenAndKill:
  73. return CLEAN
  74. def visit_call(self, op: Call) -> GenAndKill:
  75. fn = op.fn
  76. if fn.class_name and fn.name == "__init__":
  77. self_type = op.fn.sig.args[0].type
  78. assert isinstance(self_type, RInstance)
  79. cl = self_type.class_ir
  80. if not cl.init_self_leak:
  81. return CLEAN
  82. return self.check_register_op(op)
  83. def visit_method_call(self, op: MethodCall) -> GenAndKill:
  84. return self.check_register_op(op)
  85. def visit_load_error_value(self, op: LoadErrorValue) -> GenAndKill:
  86. return CLEAN
  87. def visit_load_literal(self, op: LoadLiteral) -> GenAndKill:
  88. return CLEAN
  89. def visit_get_attr(self, op: GetAttr) -> GenAndKill:
  90. cl = op.class_type.class_ir
  91. if cl.get_method(op.attr):
  92. # Property -- calls a function
  93. return self.check_register_op(op)
  94. return CLEAN
  95. def visit_set_attr(self, op: SetAttr) -> GenAndKill:
  96. cl = op.class_type.class_ir
  97. if cl.get_method(op.attr):
  98. # Property - calls a function
  99. return self.check_register_op(op)
  100. return CLEAN
  101. def visit_load_static(self, op: LoadStatic) -> GenAndKill:
  102. return CLEAN
  103. def visit_init_static(self, op: InitStatic) -> GenAndKill:
  104. return self.check_register_op(op)
  105. def visit_tuple_get(self, op: TupleGet) -> GenAndKill:
  106. return CLEAN
  107. def visit_tuple_set(self, op: TupleSet) -> GenAndKill:
  108. return self.check_register_op(op)
  109. def visit_box(self, op: Box) -> GenAndKill:
  110. return self.check_register_op(op)
  111. def visit_unbox(self, op: Unbox) -> GenAndKill:
  112. return self.check_register_op(op)
  113. def visit_cast(self, op: Cast) -> GenAndKill:
  114. return self.check_register_op(op)
  115. def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill:
  116. return CLEAN
  117. def visit_call_c(self, op: CallC) -> GenAndKill:
  118. return self.check_register_op(op)
  119. def visit_truncate(self, op: Truncate) -> GenAndKill:
  120. return CLEAN
  121. def visit_extend(self, op: Extend) -> GenAndKill:
  122. return CLEAN
  123. def visit_load_global(self, op: LoadGlobal) -> GenAndKill:
  124. return CLEAN
  125. def visit_int_op(self, op: IntOp) -> GenAndKill:
  126. return CLEAN
  127. def visit_comparison_op(self, op: ComparisonOp) -> GenAndKill:
  128. return CLEAN
  129. def visit_float_op(self, op: FloatOp) -> GenAndKill:
  130. return CLEAN
  131. def visit_float_neg(self, op: FloatNeg) -> GenAndKill:
  132. return CLEAN
  133. def visit_float_comparison_op(self, op: FloatComparisonOp) -> GenAndKill:
  134. return CLEAN
  135. def visit_load_mem(self, op: LoadMem) -> GenAndKill:
  136. return CLEAN
  137. def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill:
  138. return CLEAN
  139. def visit_load_address(self, op: LoadAddress) -> GenAndKill:
  140. return CLEAN
  141. def visit_keep_alive(self, op: KeepAlive) -> GenAndKill:
  142. return CLEAN
  143. def check_register_op(self, op: RegisterOp) -> GenAndKill:
  144. if any(src is self.self_reg for src in op.sources()):
  145. return DIRTY
  146. return CLEAN
  147. def analyze_self_leaks(
  148. blocks: list[BasicBlock], self_reg: Register, cfg: CFG
  149. ) -> AnalysisResult[None]:
  150. return run_analysis(
  151. blocks=blocks,
  152. cfg=cfg,
  153. gen_and_kill=SelfLeakedVisitor(self_reg),
  154. initial=set(),
  155. backward=False,
  156. kind=MAYBE_ANALYSIS,
  157. )