Ejemplo n.º 1
0
 def on_entity_changed(self, *args):
     self.entity_with_def = lb.get_null_handle()
     if self.entity:
         if lb.is_use(self.entity):
             used = lb.use_get_used(self.entity)
             if lb.entity_has_llvm_defn(used):
                 self.entity_with_def = used
         elif lb.is_def(self.entity):
             self.entity_with_def = lb.get_null_handle()
         elif lb.is_comdat(self.entity):
             self.entity_with_def = self.entity
Ejemplo n.º 2
0
    def on_instruction_changed(self, *args):
        def has_source(inst):
            if lb.inst_has_source_defn(inst):
                # We don't want to allow "show-source" for instructions with
                # LLVM's debug or lifetime intrinsics. These can't really be
                # mapped to anything in the source. In any case, the "useful"
                # information in those instructions is the LLVM value actually
                # passed to the call, so no point in confusing matters by
                # allowing it to be mapped to the source
                if lb.inst_is_llvm_debug_inst(inst) \
                        or lb.inst_is_llvm_lifetime_inst(inst):
                    return False
                return True
            return False

        # If an instruction was set and it has source information, then
        # set the source entity to be that instruction. If not, then
        # set it to the containing function
        self.entity_with_source = lb.get_null_handle()
        if self.inst:
            self.func = lb.inst_get_function(self.inst)
            if has_source(self.inst):
                self.entity_with_source = self.inst
            elif lb.func_has_source_defn(self.func):
                self.entity_with_source = self.func
Ejemplo n.º 3
0
    def action_mark_push(self, entity: int, offset: int) -> bool:
        if len(self.marks) == self.options.max_marks:
            return False

        if lb.is_null_handle(entity):
            # If the entity being pushed is not a use, add a mark anyway
            # This mark will be used for navigation only
            self.marks.append((lb.get_null_handle(), offset))
        else:
            uses = []
            if lb.is_use(entity):
                uses = lb.entity_get_uses(lb.use_get_used(entity))
            elif lb.is_def(entity):
                uses = lb.entity_get_uses(lb.def_get_defined(entity))
            elif lb.is_comdat(entity):
                uses = [lb.comdat_get_target(entity)]
            self.marks.append((entity, offset))
            if entity not in self.uses_indexes_map:
                self.uses_map[entity] = uses
                self.uses_indexes_map[entity] = []
            # FIXME: A better way to do this would be to set it to the use
            # nearest the offset
            self.uses_indexes_map[entity].append(0 if uses else -1)
        self.set_mark(entity, offset)
        return True
Ejemplo n.º 4
0
 def add_category(label: str) -> Gtk.TreeIter:
     return self.trst_contents.append(
         None, [lb.get_null_handle(),
                label,
                '',
                Pango.Style.NORMAL,
                Pango.Weight.BOLD,
                ''])
Ejemplo n.º 5
0
 def set_mark(self, entity: int, offset: int = 0):
     if lb.is_null_handle(entity):
         self.mark = lb.get_null_handle()
         self.mark_offset = offset
         self.mark_uses_count = 0
         self.mark_uses_index = -1
     else:
         self.mark = entity
         self.mark_offset = offset
         self.mark_uses_count = len(self.uses_map[entity])
         self.mark_uses_index = self.uses_indexes_map[entity][-1]
Ejemplo n.º 6
0
    def action_mark_pop(self) -> bool:
        if not self.marks:
            return False

        # If there is at least one mark, pop it
        if self.marks:
            entity, _ = self.marks.pop()
            # If the mark is actually a use, then remove all the metadata
            # associated with it if it is the last instance of the mark
            if not lb.is_null_handle(entity):
                self.uses_indexes_map[entity].pop()
                if len(self.uses_indexes_map[entity]) == 0:
                    del self.uses_indexes_map[entity]
                    del self.uses_map[entity]
        if self.marks:
            self.set_mark(self.marks[-1][0], self.marks[-1][1])
            # FIXME: When a mark is popped, move the cursor to the last seen use
            # of the previous mark if any
        else:
            self.set_mark(lb.get_null_handle())

        return True
Ejemplo n.º 7
0
class Application(Gtk.Application):
    module = GObject.Property(type=GHandle,
                              default=lb.get_null_handle(),
                              nick='module',
                              blurb='Handle to the LLVM module')

    llvm = GObject.Property(
        type=str,
        default='',
        nick='llvm',
        blurb='The path to the LLVM file currently shown or ""')

    entity = GObject.Property(type=GHandle,
                              default=lb.get_null_handle(),
                              nick='entity',
                              blurb=('The current entity. '
                                     'This will be a use, def or comdat'))

    inst = GObject.Property(type=GHandle,
                            default=lb.get_null_handle(),
                            nick='inst',
                            blurb=('The current instruction'))

    func = GObject.Property(type=GHandle,
                            default=lb.get_null_handle(),
                            nick='func',
                            blurb=('The current function. '
                                   'This may be set even if self.inst is not'))

    # The entity with source is used to enable the "view source" action
    # Both the current instruction and the current function could have source
    # information associated with it. In that case, we give the instruction
    # source priority if it is set. There may be some instructions in the
    # function that don't have source infromation associated with it
    # These could be LLVM intrinsic instructions for instance. In such case,
    # the function should be used as the source entity
    entity_with_source = GObject.Property(
        type=GHandle,
        default=lb.get_null_handle(),
        nick='entity-with-source',
        blurb=('Handle of the entity whose source will be shown '
               'when the show-source action is launched.'))

    # entity_with_def will be the same as self.entity
    # if self.entity is a use or a comdat. Else it will be None
    entity_with_def = GObject.Property(
        type=GHandle,
        default=lb.get_null_handle(),
        nick='entity-with-def',
        blurb=('Handle of the entity with a definition. '
               'Used when the goto definition action is launched'))

    mark = GObject.Property(
        type=GHandle,
        default=lb.get_null_handle(),
        nick='curr-mark',
        blurb=('Handle of the marked entity. '
               'This is the handle at the top of the self.marks stack'))

    mark_offset = GObject.Property(type=GObject.TYPE_UINT64,
                                   default=0,
                                   nick='mark-offset',
                                   blurb='Offset of the mark in the LLVM IR')

    mark_uses_count = GObject.Property(
        type=int,
        default=0,
        nick='mark-uses-count',
        blurb=('The number of uses for the currently marked entity'))

    mark_uses_index = GObject.Property(
        type=int,
        default=-1,
        nick='mark-uses-index',
        blurb=('The index into the use list for the currently marked entity. '
               'This is the count at the top of the self.marks stack'))

    def __init__(self):
        Gtk.Application.__init__(self)

        GLib.set_application_name('LLVM Browse')
        GLib.set_prgname('llvm-browse')

        self.argv: argparse.Namespace = None
        self.options: Options = Options(self)
        self.ui: UI = UI(self)

        # The user has to explicitly set a mark. When one is set, prev-use
        # and next-use will be enabled and the user can navigate this list
        self.marks: List[Tuple[int, int]] = []

        # A map from entities to uses. The keys are all guaranteed to be in
        # self.marks
        self.uses_map: Mapping[int, List[int]] = {}

        # Map from the entities with marks set to the index of the use that
        # was last jumped to using prev-us or next-use. Because the same
        # entity can be marked more than once, the value is a list
        self.uses_indexes_map: Mapping[int, List[int]] = {}

        self.connect('notify::entity', self.on_entity_changed)
        self.connect('notify::inst', self.on_instruction_changed)
        self.connect('notify::func', self.on_function_changed)

    def _reset(self):
        if self.module:
            lb.module_free(self.module)
        self.module = lb.get_null_handle()
        self.llvm = ''

    def do_activate(self, *args) -> bool:
        self.options.load()
        self.add_window(self.ui.get_application_window())
        self.ui.emit('launch')
        if self.argv.maximize:
            self.ui.win_main.maximize()
        if self.argv.file:
            self.action_open(self.argv.file)
        return False

    # Returns true if the file could be opened
    def action_open(self, file: str) -> bool:
        self.llvm = file
        self.module = lb.module_create(file)
        if not self.module:
            self._reset()
        else:
            self.ui.do_open()
        return bool(self.module)

    # Returns true if the file could be closed
    def action_close(self) -> bool:
        self._reset()
        return True

    # Returns true if the file could be reloaded
    def action_reload(self) -> bool:
        if self.llvm:
            llvm = self.llvm
            self._reset()
            return self.action_open(llvm)
        return False

    # Returns true on success. Not sure if this will actually return
    def action_quit(self) -> bool:
        self._reset()
        self.remove_window(self.ui.get_application_window())
        return True

    def action_goto_definition(self) -> bool:
        if self.entity_with_def:
            defn = lb.entity_get_llvm_defn(self.entity_with_def)
            offset = lb.def_get_begin(defn)
            tag = lb.entity_get_tag(self.entity_with_def)
            self.ui.do_scroll_llvm_to_offset(offset, len(tag))
            return True
        return False

    def action_goto_prev_use(self) -> bool:
        if self.marks:
            if self.mark_uses_count:
                # If there is only a single use, the "previous use" is the same
                # as the only use even if we are already at that single use
                if self.mark_uses_count > 1:
                    if self.mark_uses_index == 0:
                        self.mark_uses_index = self.mark_uses_count
                    self.mark_uses_index -= 1
                use = self.uses_map[self.mark][self.mark_uses_index]
                tag = lb.entity_get_tag(lb.use_get_used(use))
                self.ui.do_scroll_llvm_to_offset(lb.use_get_begin(use),
                                                 len(tag))
                return True
            return False
        return False

    def action_goto_next_use(self) -> bool:
        if self.marks:
            if self.mark_uses_count:
                # If there is only a single use, the "next use" is the same
                # as the only use even if we are already at that single use
                if self.mark_uses_count > 1:
                    if self.mark_uses_index == self.mark_uses_count - 1:
                        self.mark_uses_index = -1
                    self.mark_uses_index += 1
                use = self.uses_map[self.mark][self.mark_uses_index]
                tag = lb.entity_get_tag(lb.use_get_used(use))
                self.ui.do_scroll_llvm_to_offset(lb.use_get_begin(use),
                                                 len(tag))
            return False
        return False

    def action_goto_prev_mark(self) -> bool:
        pass

    def action_goto_next_mark(self) -> bool:
        pass

    def action_show_source(self) -> bool:
        if self.entity_with_source:
            self.ui.do_show_source(self.entity_with_source)
            return True
        return False

    def set_mark(self, entity: int, offset: int = 0):
        if lb.is_null_handle(entity):
            self.mark = lb.get_null_handle()
            self.mark_offset = offset
            self.mark_uses_count = 0
            self.mark_uses_index = -1
        else:
            self.mark = entity
            self.mark_offset = offset
            self.mark_uses_count = len(self.uses_map[entity])
            self.mark_uses_index = self.uses_indexes_map[entity][-1]

    def action_mark_push(self, entity: int, offset: int) -> bool:
        if len(self.marks) == self.options.max_marks:
            return False

        if lb.is_null_handle(entity):
            # If the entity being pushed is not a use, add a mark anyway
            # This mark will be used for navigation only
            self.marks.append((lb.get_null_handle(), offset))
        else:
            uses = []
            if lb.is_use(entity):
                uses = lb.entity_get_uses(lb.use_get_used(entity))
            elif lb.is_def(entity):
                uses = lb.entity_get_uses(lb.def_get_defined(entity))
            elif lb.is_comdat(entity):
                uses = [lb.comdat_get_target(entity)]
            self.marks.append((entity, offset))
            if entity not in self.uses_indexes_map:
                self.uses_map[entity] = uses
                self.uses_indexes_map[entity] = []
            # FIXME: A better way to do this would be to set it to the use
            # nearest the offset
            self.uses_indexes_map[entity].append(0 if uses else -1)
        self.set_mark(entity, offset)
        return True

    def action_mark_pop(self) -> bool:
        if not self.marks:
            return False

        # If there is at least one mark, pop it
        if self.marks:
            entity, _ = self.marks.pop()
            # If the mark is actually a use, then remove all the metadata
            # associated with it if it is the last instance of the mark
            if not lb.is_null_handle(entity):
                self.uses_indexes_map[entity].pop()
                if len(self.uses_indexes_map[entity]) == 0:
                    del self.uses_indexes_map[entity]
                    del self.uses_map[entity]
        if self.marks:
            self.set_mark(self.marks[-1][0], self.marks[-1][1])
            # FIXME: When a mark is popped, move the cursor to the last seen use
            # of the previous mark if any
        else:
            self.set_mark(lb.get_null_handle())

        return True

    def on_entity_changed(self, *args):
        self.entity_with_def = lb.get_null_handle()
        if self.entity:
            if lb.is_use(self.entity):
                used = lb.use_get_used(self.entity)
                if lb.entity_has_llvm_defn(used):
                    self.entity_with_def = used
            elif lb.is_def(self.entity):
                self.entity_with_def = lb.get_null_handle()
            elif lb.is_comdat(self.entity):
                self.entity_with_def = self.entity

    def on_instruction_changed(self, *args):
        def has_source(inst):
            if lb.inst_has_source_defn(inst):
                # We don't want to allow "show-source" for instructions with
                # LLVM's debug or lifetime intrinsics. These can't really be
                # mapped to anything in the source. In any case, the "useful"
                # information in those instructions is the LLVM value actually
                # passed to the call, so no point in confusing matters by
                # allowing it to be mapped to the source
                if lb.inst_is_llvm_debug_inst(inst) \
                        or lb.inst_is_llvm_lifetime_inst(inst):
                    return False
                return True
            return False

        # If an instruction was set and it has source information, then
        # set the source entity to be that instruction. If not, then
        # set it to the containing function
        self.entity_with_source = lb.get_null_handle()
        if self.inst:
            self.func = lb.inst_get_function(self.inst)
            if has_source(self.inst):
                self.entity_with_source = self.inst
            elif lb.func_has_source_defn(self.func):
                self.entity_with_source = self.func

    def on_function_changed(self, *args):
        # If a function was set, then check if an instruction has also been
        # set. If the instruction has not been set, then set the source entity
        # to be the function if it has source information
        if self.func:
            if (not self.inst) and lb.func_has_source_defn(self.func):
                self.entity_with_source = self.func

    def run(self, argv: argparse.Namespace) -> int:
        self.argv = argv
        ret = Gtk.Application.run(self)
        self.options.store()
        return ret
Ejemplo n.º 8
0
 def _reset(self):
     if self.module:
         lb.module_free(self.module)
     self.module = lb.get_null_handle()
     self.llvm = ''