<commit_before>
from .arch import Arch, register_arch, Endness
import re

import logging
l = logging.getLogger('archinfo.arch_soot')


class SootMethodDescriptor(object):
    def __init__(self, class_name, name, params, attrs=[], ret=None, exceptions=()):
        self.class_name = class_name
        self.name = name
        self.params = params
        self.attrs = attrs
        self.ret = ret
        self.exceptions=()

    def __repr__(self):
        return "%s.%s(%s)" % (self.class_name, self.name, ",".join(self.params))

    def __hash__(self):
        return hash((self.class_name, self.name, self.params))

    def __eq__(self, other):
        return isinstance(other, SootMethodDescriptor) and \
                self.class_name == other.class_name and \
                self.name == other.name and \
                self.params == other.params

    def __ne__(self, other):
        return not self == other

    @classmethod
    def from_string(cls, tstr):
        # this should be the opposite of repr
        tstr = tstr.strip()
        class_and_method, tparams = tstr.split("(")
        params_str = tparams.split(")")[0]
        if params_str == "":
            params = tuple()
        else:
            params = tuple([t.strip() for t in params_str.split(",")])
        class_name, _, method = class_and_method.rpartition(".")
        return cls(class_name, method, params)

    @property
    def fullname(self):
        return "%s.%s" % (self.class_name, self.name)

    def matches_with_native_name(self, native_method):
        """
        Name of native methods are getting encoded, s.t. they translate into valid C function names.
        :return: True, if name of soot method matches the mangled native name.
        """
        
        if "__" in native_method:
            # if native methods are overloaded, two underscores are used
            # TODO check argument signature
            raise NotImplementedError('Overloaded native methods are not yet supported.')

        # demangle native name
        native_method =  native_method.replace('_1', '_')
        # TODO unicode escaping

        method_native_name = "Java_{class_name}_{method_name}".format(class_name=self.class_name,
                                                                      method_name=self.name)
        return native_method == method_native_name

    @classmethod
    def from_method(cls, method):
        return cls(class_name=method.class_name, 
                   name=method.name, 
                   params=method.params, 
                   attrs=method.attrs,
                   ret=method.ret,
                   exceptions=method.exceptions)

    @classmethod
    def from_soot_method(cls, soot_method):
        return cls(class_name=str(soot_method.class_name), 
                   name=str(soot_method.name), 
                   params=soot_method.params, 
                   attrs=soot_method.attrs,
                   ret=soot_method.ret,
                   exceptions=soot_method.exceptions)

    @property
    def symbolic(self):
        return False


class SootAddressDescriptor(object):
    def __init__(self, method, block_idx, stmt_idx):

        if not isinstance(method, SootMethodDescriptor):
            raise ValueError('The parameter "method" must be an instance of SootMethodDescriptor.')

        self.method = method
        self.block_idx = block_idx
        self.stmt_idx = stmt_idx

    def __repr__(self):
        return "<%s+(%s:%s)>" % (repr(self.method),
                               self.block_idx,
                               '%d' % self.stmt_idx if self.stmt_idx is not None else '[0]'
                               )

    def __hash__(self):
        return hash((self.method, self.stmt_idx))

    def __eq__(self, other):
        # We do not compare the block IDs since statement IDs are unique enough
        return isinstance(other, SootAddressDescriptor) and \
                self.method == other.method and \
                self.stmt_idx == other.stmt_idx

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        if not isinstance(other, SootAddressDescriptor):
            raise TypeError("You cannot compare a SootAddressDescriptor with a %s." % type(other))

        if self.method != other.method:
            raise ValueError("You cannot compare two SootAddressDescriptor instances of two different methods.")

        return self.stmt_idx < other.stmt_idx

    def __le__(self, other):
        if not isinstance(other, SootAddressDescriptor):
            raise TypeError("You cannot compare a SootAddressDescriptor with a %s." % type(other))

        if self.method != other.method:
            raise ValueError("You cannot compare two SootAddressDescriptor instances of two different methods.")

        return self.stmt_idx <= other.stmt_idx

    def copy(self):
        return SootAddressDescriptor(method=self.method,
                                     block_idx=self.block_idx,
                                     stmt_idx=self.stmt_idx
                                     )

    @property
    def symbolic(self):
        return False

    def __add__(self, stmts_offset):

        if not isinstance(stmts_offset, (int, long)):
            raise TypeError('The stmts_offset must be an int or a long.')

        s = self.copy()
        s.stmt_idx += stmts_offset
        return s


class SootAddressTerminator(SootAddressDescriptor):
    def __init__(self):
        super(SootAddressTerminator, self).__init__(SootMethodDescriptor("dummy", "dummy", tuple()), 0, 0)

    def __repr__(self):
        return "<Terminator>"

class SootFieldDescriptor(object):
    def __init__(self, class_name, name, type_):
        self.class_name = class_name
        self.name = name
        self.type = type_

    def __repr__(self):
        return "%s.%s" % (self.class_name, self.name)

    def __hash__(self):
        return hash((self.class_name, self.name, self.type))

    def __eq__(self, other):
        return isinstance(other, SootFieldDescriptor) and \
                self.class_name == other.class_name and \
                self.name == other.name and \
                self.type == other.type

    def __ne__(self, other):
        return not self == other



class ArchSoot(Arch):
    def __init__(self, endness=Endness.LE):
        super(ArchSoot, self).__init__(endness)

    name = 'Soot'

    vex_arch = None  # No VEX support
    qemu_name = None  # No Qemu/Unicorn-engine support
    bits = 64
    address_types = (SootAddressDescriptor, )
    function_address_types = (SootMethodDescriptor, )

    # Size of native counterparts of primitive Java types
    sizeof = {'boolean'  :  8,
              'byte'     :  8,
              'char'     : 16, 
              'short'    : 16,
              'int'      : 32,
              'long'     : 64, 
              'float'    : 32,
              'double'   : 64
             }<commit_msg>Improved management of method- / class-descriptor<commit_after>
from .arch import Arch, register_arch, Endness
import re

import logging
l = logging.getLogger('archinfo.arch_soot')


class SootMethodDescriptor(object):
    def __init__(self, class_name, name, params, soot_method=None):
        self.class_name = class_name
        self.name = name
        self.params = params
        self._soot_method = soot_method

    def __repr__(self):
        return "%s.%s(%s)" % (self.class_name, self.name, ",".join(self.params))

    def __hash__(self):
        return hash((self.class_name, self.name, self.params))

    def __eq__(self, other):
        return isinstance(other, SootMethodDescriptor) and \
                self.class_name == other.class_name and \
                self.name == other.name and \
                self.params == other.params

    def __ne__(self, other):
        return not self == other

    @classmethod
    def from_string(cls, tstr):
        # this should be the opposite of repr
        tstr = tstr.strip()
        class_and_method, tparams = tstr.split("(")
        params_str = tparams.split(")")[0]
        if params_str == "":
            params = tuple()
        else:
            params = tuple([t.strip() for t in params_str.split(",")])
        class_name, _, method = class_and_method.rpartition(".")
        return cls(class_name, method, params)

    @property
    def fullname(self):
        return "%s.%s" % (self.class_name, self.name)

    def matches_with_native_name(self, native_method):
        """
        Name of native methods are getting encoded, s.t. they translate into valid C function names.
        :return: True, if name of soot method matches the mangled native name.
        """
        
        if "__" in native_method:
            # if native methods are overloaded, two underscores are used
            # TODO check function signature
            raise NotImplementedError('Overloaded native methods are not supported.')

        # demangle native name
        native_method =  native_method.replace('_1', '_')
        # TODO unicode escaping

        method_native_name = "Java_{class_name}_{method_name}".format(
                              class_name=self.class_name,
                              method_name=self.name)
        return native_method == method_native_name

    @classmethod
    def from_soot_method(cls, soot_method):
        return cls(class_name=soot_method.class_name, 
                   name=soot_method.name, 
                   params=soot_method.params, 
                   soot_method=soot_method)

    @property
    def symbolic(self):
        return False
    
    @property 
    def is_loaded(self):
        """
        :return: True, if the method is loaded in CLE and thus
                 info about attrs, ret and exception are available.
        """
        return self._soot_method != None
    
    @property
    def attrs(self):
        return self._soot_method.attrs if self.is_loaded else []

    @property
    def exceptions(self):
        return self._soot_method.exceptions if self.is_loaded else []

    @property
    def ret(self):
        return self._soot_method.ret if self.is_loaded else []


class SootAddressDescriptor(object):
    def __init__(self, method, block_idx, stmt_idx):

        if not isinstance(method, SootMethodDescriptor):
            raise ValueError('The parameter "method" must be an instance of SootMethodDescriptor.')

        self.method = method
        self.block_idx = block_idx
        self.stmt_idx = stmt_idx

    def __repr__(self):
        return "<%s+(%s:%s)>" % (repr(self.method),
                               self.block_idx,
                               '%d' % self.stmt_idx if self.stmt_idx is not None else '[0]'
                               )

    def __hash__(self):
        return hash((self.method, self.stmt_idx))

    def __eq__(self, other):
        # We do not compare the block IDs since statement IDs are unique enough
        return isinstance(other, SootAddressDescriptor) and \
                self.method == other.method and \
                self.stmt_idx == other.stmt_idx

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        if not isinstance(other, SootAddressDescriptor):
            raise TypeError("You cannot compare a SootAddressDescriptor with a %s." % type(other))

        if self.method != other.method:
            raise ValueError("You cannot compare two SootAddressDescriptor instances of two different methods.")

        return self.stmt_idx < other.stmt_idx

    def __le__(self, other):
        if not isinstance(other, SootAddressDescriptor):
            raise TypeError("You cannot compare a SootAddressDescriptor with a %s." % type(other))

        if self.method != other.method:
            raise ValueError("You cannot compare two SootAddressDescriptor instances of two different methods.")

        return self.stmt_idx <= other.stmt_idx

    def copy(self):
        return SootAddressDescriptor(method=self.method,
                                     block_idx=self.block_idx,
                                     stmt_idx=self.stmt_idx
                                     )

    @property
    def symbolic(self):
        return False

    def __add__(self, stmts_offset):

        if not isinstance(stmts_offset, (int, long)):
            raise TypeError('The stmts_offset must be an int or a long.')

        s = self.copy()
        s.stmt_idx += stmts_offset
        return s


class SootAddressTerminator(SootAddressDescriptor):
    def __init__(self):
        super(SootAddressTerminator, self).__init__(SootMethodDescriptor("dummy", "dummy", tuple()), 0, 0)

    def __repr__(self):
        return "<Terminator>"


class SootFieldDescriptor(object):
    def __init__(self, class_name, name, type_):
        self.class_name = class_name
        self.name = name
        self.type = type_

    def __repr__(self):
        return "%s.%s" % (self.class_name, self.name)

    def __hash__(self):
        return hash((self.class_name, self.name, self.type))

    def __eq__(self, other):
        return isinstance(other, SootFieldDescriptor) and \
                self.class_name == other.class_name and \
                self.name == other.name and \
                self.type == other.type

    def __ne__(self, other):
        return not self == other


class SootClassDescriptor(object):
    
    def __init__(self, name, soot_class=None):
        self.name = name
        self._soot_class = soot_class

    def __repr__(self):
        return self.name

    def __hash__(self):
        return hash(self.name)

    def __eq__(self, other):
        return isinstance(other, SootClassDescriptor) and \
               self.name == other.name

    def __ne__(self, other):
        return not self == other

    @property
    def is_loaded(self):
        """
        :return: True, if the class is loaded in CLE and thus
                 info about field, methods, ... are available.
        """
        return self._soot_class != None

    @property
    def fields(self):
        return self._soot_class.fields if self.is_loaded else None
  
    @property
    def methods(self):
        return self._soot_class.methods if self.is_loaded else None
    
    @property
    def superclass_name(self):
        return self._soot_class.super_class if self.is_loaded else None


class ArchSoot(Arch):
    def __init__(self, endness=Endness.LE):
        super(ArchSoot, self).__init__(endness)

    name = 'Soot'

    vex_arch = None  # No VEX support
    qemu_name = None  # No Qemu/Unicorn-engine support
    bits = 64
    address_types = (SootAddressDescriptor, )
    function_address_types = (SootMethodDescriptor, )

    # Size of native counterparts of primitive Java types
    sizeof = {'boolean'  :  8,
              'byte'     :  8,
              'char'     : 16, 
              'short'    : 16,
              'int'      : 32,
              'long'     : 64, 
              'float'    : 32,
              'double'   : 64
             }
{'fraction_non_alphanumeric': 0.09378531073446328, 'fraction_numerical': 0.0027916251246261218, 'mean_word_length': 2.05502538071066, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 9, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '195887', 'n_tokens_mistral': 4312, 'n_tokens_neox': 4119, 'n_words': 1127}