from ctypes import c_char_p
from ctypes import c_char
from ctypes import POINTER
from ctypes import c_uint64
from ctypes import string_at
from .common import CachedProperty
from .common import LLVMObject
from .common import c_object_p
from .common import get_library
from .core import MemoryBuffer
__all__ = [
    "lib",
    "ObjectFile",
    "Relocation",
    "Section",
    "Symbol",
]
class ObjectFile(LLVMObject):
    
    def __init__(self, filename=None, contents=None):
        
        if contents:
            assert isinstance(contents, MemoryBuffer)
        if filename is not None:
            contents = MemoryBuffer(filename=filename)
        if contents is None:
            raise Exception('No input found.')
        ptr = lib.LLVMCreateObjectFile(contents)
        LLVMObject.__init__(self, ptr, disposer=lib.LLVMDisposeObjectFile)
        self.take_ownership(contents)
    def get_sections(self, cache=False):
        
        sections = lib.LLVMGetSections(self)
        last = None
        while True:
            if lib.LLVMIsSectionIteratorAtEnd(self, sections):
                break
            last = Section(sections)
            if cache:
                last.cache()
            yield last
            lib.LLVMMoveToNextSection(sections)
            last.expire()
        if last is not None:
            last.expire()
        lib.LLVMDisposeSectionIterator(sections)
    def get_symbols(self, cache=False):
        
        symbols = lib.LLVMGetSymbols(self)
        last = None
        while True:
            if lib.LLVMIsSymbolIteratorAtEnd(self, symbols):
                break
            last = Symbol(symbols, self)
            if cache:
                last.cache()
            yield last
            lib.LLVMMoveToNextSymbol(symbols)
            last.expire()
        if last is not None:
            last.expire()
        lib.LLVMDisposeSymbolIterator(symbols)
class Section(LLVMObject):
    
    def __init__(self, ptr):
        
        LLVMObject.__init__(self, ptr)
        self.expired = False
    @CachedProperty
    def name(self):
        
        if self.expired:
            raise Exception('Section instance has expired.')
        return lib.LLVMGetSectionName(self)
    @CachedProperty
    def size(self):
        
        if self.expired:
            raise Exception('Section instance has expired.')
        return lib.LLVMGetSectionSize(self)
    @CachedProperty
    def contents(self):
        if self.expired:
            raise Exception('Section instance has expired.')
        siz = self.size
        r = lib.LLVMGetSectionContents(self)
        if r:
            return string_at(r, siz)
        return None
    @CachedProperty
    def address(self):
        
        if self.expired:
            raise Exception('Section instance has expired.')
        return lib.LLVMGetSectionAddress(self)
    def has_symbol(self, symbol):
        
        if self.expired:
            raise Exception('Section instance has expired.')
        assert isinstance(symbol, Symbol)
        return lib.LLVMGetSectionContainsSymbol(self, symbol)
    def get_relocations(self, cache=False):
        
        if self.expired:
            raise Exception('Section instance has expired.')
        relocations = lib.LLVMGetRelocations(self)
        last = None
        while True:
            if lib.LLVMIsRelocationIteratorAtEnd(self, relocations):
                break
            last = Relocation(relocations)
            if cache:
                last.cache()
            yield last
            lib.LLVMMoveToNextRelocation(relocations)
            last.expire()
        if last is not None:
            last.expire()
        lib.LLVMDisposeRelocationIterator(relocations)
    def cache(self):
        
        getattr(self, 'name')
        getattr(self, 'size')
        getattr(self, 'contents')
        getattr(self, 'address')
    def expire(self):
        
        self.expired = True
class Symbol(LLVMObject):
    
    def __init__(self, ptr, object_file):
        assert isinstance(ptr, c_object_p)
        assert isinstance(object_file, ObjectFile)
        LLVMObject.__init__(self, ptr)
        self.expired = False
        self._object_file = object_file
    @CachedProperty
    def name(self):
        
        if self.expired:
            raise Exception('Symbol instance has expired.')
        return lib.LLVMGetSymbolName(self)
    @CachedProperty
    def address(self):
        
        if self.expired:
            raise Exception('Symbol instance has expired.')
        return lib.LLVMGetSymbolAddress(self)
    @CachedProperty
    def size(self):
        
        if self.expired:
            raise Exception('Symbol instance has expired.')
        return lib.LLVMGetSymbolSize(self)
    @CachedProperty
    def section(self):
        
        sections = lib.LLVMGetSections(self._object_file)
        lib.LLVMMoveToContainingSection(sections, self)
        return Section(sections)
    def cache(self):
        
        getattr(self, 'name')
        getattr(self, 'address')
        getattr(self, 'size')
    def expire(self):
        
        self.expired = True
class Relocation(LLVMObject):
    
    def __init__(self, ptr):
        
        assert isinstance(ptr, c_object_p)
        LLVMObject.__init__(self, ptr)
        self.expired = False
    @CachedProperty
    def offset(self):
        
        if self.expired:
            raise Exception('Relocation instance has expired.')
        return lib.LLVMGetRelocationOffset(self)
    @CachedProperty
    def symbol(self):
        
        if self.expired:
            raise Exception('Relocation instance has expired.')
        ptr = lib.LLVMGetRelocationSymbol(self)
        return Symbol(ptr)
    @CachedProperty
    def type_number(self):
        
        if self.expired:
            raise Exception('Relocation instance has expired.')
        return lib.LLVMGetRelocationType(self)
    @CachedProperty
    def type_name(self):
        
        if self.expired:
            raise Exception('Relocation instance has expired.')
        return lib.LLVMGetRelocationTypeName(self)
    @CachedProperty
    def value_string(self):
        if self.expired:
            raise Exception('Relocation instance has expired.')
        return lib.LLVMGetRelocationValueString(self)
    def expire(self):
        
        self.expired = True
    def cache(self):
        
        getattr(self, 'address')
        getattr(self, 'offset')
        getattr(self, 'symbol')
        getattr(self, 'type')
        getattr(self, 'type_name')
        getattr(self, 'value_string')
def register_library(library):
    
        library.LLVMCreateObjectFile.argtypes = [MemoryBuffer]
    library.LLVMCreateObjectFile.restype = c_object_p
    library.LLVMDisposeObjectFile.argtypes = [ObjectFile]
    library.LLVMGetSections.argtypes = [ObjectFile]
    library.LLVMGetSections.restype = c_object_p
    library.LLVMDisposeSectionIterator.argtypes = [c_object_p]
    library.LLVMIsSectionIteratorAtEnd.argtypes = [ObjectFile, c_object_p]
    library.LLVMIsSectionIteratorAtEnd.restype = bool
    library.LLVMMoveToNextSection.argtypes = [c_object_p]
    library.LLVMMoveToContainingSection.argtypes = [c_object_p, c_object_p]
    library.LLVMGetSymbols.argtypes = [ObjectFile]
    library.LLVMGetSymbols.restype = c_object_p
    library.LLVMDisposeSymbolIterator.argtypes = [c_object_p]
    library.LLVMIsSymbolIteratorAtEnd.argtypes = [ObjectFile, c_object_p]
    library.LLVMIsSymbolIteratorAtEnd.restype = bool
    library.LLVMMoveToNextSymbol.argtypes = [c_object_p]
    library.LLVMGetSectionName.argtypes = [c_object_p]
    library.LLVMGetSectionName.restype = c_char_p
    library.LLVMGetSectionSize.argtypes = [c_object_p]
    library.LLVMGetSectionSize.restype = c_uint64
    library.LLVMGetSectionContents.argtypes = [c_object_p]
        library.LLVMGetSectionContents.restype = POINTER(c_char)
    library.LLVMGetSectionAddress.argtypes = [c_object_p]
    library.LLVMGetSectionAddress.restype = c_uint64
    library.LLVMGetSectionContainsSymbol.argtypes = [c_object_p, c_object_p]
    library.LLVMGetSectionContainsSymbol.restype = bool
    library.LLVMGetRelocations.argtypes = [c_object_p]
    library.LLVMGetRelocations.restype = c_object_p
    library.LLVMDisposeRelocationIterator.argtypes = [c_object_p]
    library.LLVMIsRelocationIteratorAtEnd.argtypes = [c_object_p, c_object_p]
    library.LLVMIsRelocationIteratorAtEnd.restype = bool
    library.LLVMMoveToNextRelocation.argtypes = [c_object_p]
    library.LLVMGetSymbolName.argtypes = [Symbol]
    library.LLVMGetSymbolName.restype = c_char_p
    library.LLVMGetSymbolAddress.argtypes = [Symbol]
    library.LLVMGetSymbolAddress.restype = c_uint64
    library.LLVMGetSymbolSize.argtypes = [Symbol]
    library.LLVMGetSymbolSize.restype = c_uint64
    library.LLVMGetRelocationOffset.argtypes = [c_object_p]
    library.LLVMGetRelocationOffset.restype = c_uint64
    library.LLVMGetRelocationSymbol.argtypes = [c_object_p]
    library.LLVMGetRelocationSymbol.restype = c_object_p
    library.LLVMGetRelocationType.argtypes = [c_object_p]
    library.LLVMGetRelocationType.restype = c_uint64
    library.LLVMGetRelocationTypeName.argtypes = [c_object_p]
    library.LLVMGetRelocationTypeName.restype = c_char_p
    library.LLVMGetRelocationValueString.argtypes = [c_object_p]
    library.LLVMGetRelocationValueString.restype = c_char_p
lib = get_library()
register_library(lib)