text: import logging
  
  from . import Analysis
  
  l = logging.getLogger(name=__name__)
  
  
  class SootClassHierarchyError(Exception):
      def __init__(self, msg):
          self.msg = msg
  
      def __str__(self):
          return self.msg
  
  
  class NoConcreteDispatch(SootClassHierarchyError):
      def __init__(self, msg):
          self.msg = msg
  
  
  class SootClassHierarchy(Analysis):
      """
      Generate complete hierarchy.
      """
  
      def __init__(self):
  
          if self.project.arch.name != 'Soot':
              raise SootClassHierarchyError('SootClassHierarchyError only supports analyzing Soot programs.')
  
          self.interface_implementers = {}
          self.sub_interfaces = {}
          self.dir_sub_interfaces = {}
          self.sub_classes = {}
          self.dir_sub_classes = {}
          # init data
          self.init_hierarchy()
  
      def init_hierarchy(self):
          for class_name, cls in self.project.loader.main_object.classes.items():
  
              if 'INTERFACE' in cls.attrs:
                  self.interface_implementers[cls] = []
                  self.dir_sub_interfaces[cls] = []
              else:
                  self.dir_sub_classes[cls] = []
  
          for class_name, cls in self.project.loader.main_object.classes.items():
              if self.has_super_class(cls):
                  if 'INTERFACE' in cls.attrs:
                      # TODO
                      # super_interfaces
                      pass
  
                  else:
                      super_class = self.project.loader.main_object.classes[cls.super_class]
                      self.dir_sub_classes[super_class].append(cls)
  
                      for i_name in cls.interfaces:
                          # get interface
                          if i_name not in self.project.loader.main_object.classes:
                              continue
                          i = self.project.loader.main_object.classes[i_name]
                          self.interface_implementers[i].append(cls)
  
          # fill direct implementers with subclasses
          for class_name, cls in self.project.loader.main_object.classes.items():
              if 'INTERFACE' in cls.attrs:
                  implementers = self.interface_implementers[cls]
                  s = set()
  
                  for c in implementers:
                      s |= set(self.get_sub_classes_including(c))
  
                  self.interface_implementers[cls] = list(s)
  
      def has_super_class(self, cls):
          if cls.super_class:
              try:
                  self.project.loader.main_object.classes[cls.super_class]
                  return True
              except KeyError:
                  pass
  
          return False
  
      def is_subclass_including(self, cls_child, cls_parent):
          parent_classes = self.get_super_classes_including(cls_child)
  
          if cls_parent in parent_classes:
              return True
  
          return False
  
      def is_subclass(self, cls_child, cls_parent):
          parent_classes = self.get_super_classes(cls_child)
  
          if cls_parent in parent_classes:
              return True
  
          return False
  
      def is_visible_method(self, cls, method):
          method_cls = self.project.loader.main_object.classes[method.class_name]
  
          if not self.is_visible_class(cls, method_cls):
              return False
  
          if 'PUBLIC' in method.attrs:
              return True
  
          if 'PRIVATE' in method.attrs:
              return cls == method_cls
  
          package_from = cls.name.split('.')[:-1]
          package_to = method_cls.name.split('.')[:-1]
  
          if 'PROTECTED' in method.attrs:
              is_sub = self.is_subclass_including(cls, method_cls)
              is_same_package = package_from == package_to
              return is_sub or is_same_package
  
          return package_from == package_to
  
      def is_visible_class(self, cls_from, cls_to):
          if 'PUBLIC' in cls_to.attrs:
              return True
  
          if 'PROTECTED' in cls_to.attrs or 'PRIVATE' in cls_to.attrs:
              return False
  
          package_from = cls_from.name.split('.')[:-1]
          package_to = cls_to.name.split('.')[:-1]
          return package_from == package_to
  
      def get_super_classes(self, cls):
          if 'INTERFACE' in cls.attrs:
              raise SootClassHierarchyError('This is an Interface')
  
          super_classes = []
  
          current = cls
          try:
              while True:
                  current = self.project.loader.main_object.classes[current.super_class]
                  super_classes.append(current)
  
          except KeyError:
              return super_classes
  
      def get_super_classes_including(self, cls):
          super_classes = self.get_super_classes(cls)
          res = []
  
          res.append(cls)
          res.extend(super_classes)
  
          return res
  
      def get_implementers(self, interface):
          if 'INTERFACE' not in interface.attrs:
              raise SootClassHierarchyError('This is not an interface')
  
          res_set = set()
  
          for i in self.get_sub_interfaces_including(interface):
              res_set |= set(self.interface_implementers[i])
  
          return list(res_set)
  
      def get_sub_interfaces_including(self, interface):
          res = self.get_sub_interfaces(interface)
          res.append(interface)
  
          return res
  
      def get_sub_interfaces(self, interface):
          if 'INTERFACE' not in interface.attrs:
              raise SootClassHierarchyError('This is not an interface')
  
          if interface in self.sub_interfaces:
              return self.sub_interfaces[interface]
  
          # Otherwise
          res = []
          for i in self.dir_sub_interfaces[interface]:
              res.extend(self.get_sub_interfaces_including(i))
  
          self.sub_interfaces[interface] = res
          return res
  
      def get_sub_classes(self, cls):
          if 'INTERFACE' in cls.attrs:
              raise SootClassHierarchyError('This is an Interface. Class needed')
  
          if cls in self.sub_classes:
              return self.sub_classes[cls]
  
          res = []
          for c in self.dir_sub_classes[cls]:
              res.extend(self.get_sub_classes_including(c))
  
          self.sub_classes[cls] = res
          return res
  
      def get_sub_classes_including(self, cls):
          if 'INTERFACE' in cls.attrs:
              raise SootClassHierarchyError('This is an Interface. Class needed')
  
          res = []
          res.extend(self.get_sub_classes(cls))
          res.append(cls)
  
          return res
  
      def resolve_abstract_dispatch(self, cls, method):
          if 'INTERFACE' in cls.attrs:
              classes_set = set()
              for i in self.get_implementers(cls):
                  classes_set |= set(self.get_sub_classes_including(i))
              classes = list(classes_set)
          else:
              classes = self.get_sub_classes_including(cls)
  
          res_set = set()
          for c in classes:
              if 'ABSTRACT' not in c.attrs:
                  res_set.add(self.resolve_concrete_dispatch(c, method))
  
          return list(res_set)
  
      def resolve_concrete_dispatch(self, cls, method):
          if 'INTERFACE' in cls.attrs:
              raise SootClassHierarchyError('class needed!')
  
          for c in self.get_super_classes_including(cls):
              for m in c.methods:
                  if m.name == method.name and m.params == method.params:
                      if self.is_visible_method(c, method):
                          return m
  
          raise NoConcreteDispatch('Could not resolve concrete dispatch!')
  
      def resolve_special_dispatch(self, method, container):
          # container is the method that contains the invoke
          method_cls = self.project.loader.main_object.classes[method.class_name]
          container_cls = self.project.loader.main_object.classes[container.class_name]
  
          if method.name == '<init>' or 'PRIVATE' in method.attrs:
              return method
  
          elif self.is_subclass(method_cls, container_cls):
              return self.resolve_concrete_dispatch(container_cls, method)
  
          else:
              return method
  
      def resolve_invoke(self, invoke_expr, method, container):
          # Generic method to resolve invoke
          # Given an invoke expression it figures out which "technique" should be applied
          invoke_type = str(type(invoke_expr))
          cls = self.project.loader.main_object.classes[method.class_name]
  
          if 'VirtualInvokeExpr' in invoke_type:
              targets = self.resolve_abstract_dispatch(cls, method)
  
          elif 'DynamicInvokeExpr' in invoke_type:
              targets = self.resolve_abstract_dispatch(cls, method)
  
          elif 'InterfaceInvokeExpr' in invoke_type:
              targets = self.resolve_abstract_dispatch(cls, method)
  
          elif 'SpecialInvokeExpr' in invoke_type:
              t = self.resolve_special_dispatch(method, container)
              targets = [t]
  
          elif 'StaticInvokeExpr' in invoke_type:
              targets = [method]
  
          return targets
  
  
  from angr.analyses import AnalysesHub
  AnalysesHub.register_default('SootClassHierarchy', SootClassHierarchy)
  