コード例 #1
0
ファイル: call_tree_manager.py プロジェクト: rknuus/INCode
 def dump(self, file_name, entry_point, include_system_headers=False, extra_arguments=None):
     tu_access = ClangTUAccess(file_name=file_name, extra_arguments=extra_arguments)
     self.call_graph_access_ = ClangCallGraphAccess(include_system_headers=include_system_headers)
     for file_name, compiler_arguments in tu_access.files.items():
         self.call_graph_access_.parse_tu(tu_file_name=file_name, compiler_arguments=compiler_arguments)
     root = self.call_graph_access_.get_callable(entry_point)
     return self.dump_callable_(root, 0)
コード例 #2
0
def test__given_recursive_function_call__parse_tu_contains_recursion():
    access = ClangCallGraphAccess()
    with generate_file('two-functions.cpp', 'void f();\nvoid f() {f();}') as file_name:
        access.parse_tu(tu_file_name=file_name, compiler_arguments='')
    expected = ['f()']
    actual = get_names_of_calls(access.get_calls_of('f()'))
    assert expected == actual
コード例 #3
0
def test_given_unknown_entry_point__get_empty_calls_list():
    access = ClangCallGraphAccess()
    with generate_file('one-function.cpp', 'void f() {}') as file_name:
        access.parse_tu(tu_file_name=file_name, compiler_arguments='')
    expected = []
    actual = access.get_calls_of('g()')
    assert expected == actual
コード例 #4
0
def test__given_parsed_tu_with_one_function__get_callable_returns_callable_with_expected_name():
    access = ClangCallGraphAccess()
    with generate_file('one-function.cpp', 'void f() {}') as file_name:
        access.parse_tu(tu_file_name=file_name, compiler_arguments='')
        callable = access.get_callable('f()')
    expected = 'f()'
    actual = callable.name
    assert expected == actual
コード例 #5
0
ファイル: call_tree_manager.py プロジェクト: rknuus/INCode
 def select_tu(self, file_name):
     if self.state_ not in [CallTreeManagerState.READY_TO_SELECT_TU,
                            CallTreeManagerState.READY_TO_SELECT_ROOT]:
         warnings.warn('Unsupported state transition from {} to {}'.format(
             self.state_, CallTreeManagerState.READY_TO_SELECT_ROOT))
         return
     if file_name not in self.tu_access_.files:
         warnings.warn('File {} not found in compilation database'.format(file_name))
         return
     compiler_arguments = self.tu_access_.files[file_name]
     self.call_graph_access_ = ClangCallGraphAccess(include_system_headers=self.include_system_headers_)
     self.call_graph_access_.parse_tu(tu_file_name=file_name, compiler_arguments=compiler_arguments)
     self.loaded_files_.add(file_name)
     self.state_ = CallTreeManagerState.READY_TO_SELECT_ROOT
     return self.call_graph_access_.get_callables_in(file_name)
コード例 #6
0
ファイル: call_tree_manager.py プロジェクト: rknuus/INCode
class CallTreeManager(object):
    ''' Manages call-tree related use cases '''
    def __init__(self):
        super(CallTreeManager, self).__init__()
        self.extra_arguments_ = ''
        self.tu_access_ = None
        self.call_graph_access_ = None
        self.include_system_headers_ = False
        self.loaded_files_ = set()
        self.included_ = set()
        self.root_ = None
        self.state_ = CallTreeManagerState.INITIALIZED

    def set_extra_arguments(self, extra_arguments, include_system_headers=False):
        if self.state_ not in [CallTreeManagerState.INITIALIZED,
                               CallTreeManagerState.EXTRA_ARGUMENTS_INITIALIZED]:
            warnings.warn('Unsupported state transition from {} to {}'.format(
                self.state_, CallTreeManagerState.EXTRA_ARGUMENTS_INITIALIZED))
            return
        self.extra_arguments_ = extra_arguments
        self.include_system_headers_ = include_system_headers
        self.state_ = CallTreeManagerState.EXTRA_ARGUMENTS_INITIALIZED

    def open(self, file_name):
        if self.state_ not in [CallTreeManagerState.INITIALIZED,
                               CallTreeManagerState.EXTRA_ARGUMENTS_INITIALIZED,
                               CallTreeManagerState.READY_TO_SELECT_TU]:
            warnings.warn('Unsupported state transition from {} to {}'.format(
                self.state_, CallTreeManagerState.READY_TO_SELECT_TU))
            return
        self.tu_access_ = ClangTUAccess(file_name=file_name,
                                        extra_arguments=self.extra_arguments_)
        set_global_common_path(find_common_path(list(self.tu_access_.files)))
        self.state_ = CallTreeManagerState.READY_TO_SELECT_TU
        return self.tu_access_.files.keys()

    def select_tu(self, file_name):
        if self.state_ not in [CallTreeManagerState.READY_TO_SELECT_TU,
                               CallTreeManagerState.READY_TO_SELECT_ROOT]:
            warnings.warn('Unsupported state transition from {} to {}'.format(
                self.state_, CallTreeManagerState.READY_TO_SELECT_ROOT))
            return
        if file_name not in self.tu_access_.files:
            warnings.warn('File {} not found in compilation database'.format(file_name))
            return
        compiler_arguments = self.tu_access_.files[file_name]
        self.call_graph_access_ = ClangCallGraphAccess(include_system_headers=self.include_system_headers_)
        self.call_graph_access_.parse_tu(tu_file_name=file_name, compiler_arguments=compiler_arguments)
        self.loaded_files_.add(file_name)
        self.state_ = CallTreeManagerState.READY_TO_SELECT_ROOT
        return self.call_graph_access_.get_callables_in(file_name)

    def select_root(self, callable_name):
        if self.state_ not in [CallTreeManagerState.READY_TO_SELECT_ROOT,
                               CallTreeManagerState.READY_FOR_INTERACTIONS]:
            warnings.warn('Unsupported state transition from {} to {}'.format(
                self.state_, CallTreeManagerState.READY_FOR_INTERACTIONS))
            return
        self.root_ = self.call_graph_access_.get_callable(callable_name)
        self.state_ = CallTreeManagerState.READY_FOR_INTERACTIONS
        return self.root_

    def load_definition(self, callable_name):
        for file_name, compiler_arguments in self.list_tu_candidates_(callable_name).items():
            if file_name not in self.loaded_files_:
                self.call_graph_access_.parse_tu(tu_file_name=file_name, compiler_arguments=compiler_arguments)
                self.loaded_files_.add(file_name)
                callable = self.call_graph_access_.get_callable(callable_name)
                if callable and callable.is_definition():
                    return callable

    def get_calls_of(self, callable_name):
        return self.call_graph_access_.get_calls_of(callable_name)

    def include(self, callable_name):
        self.included_.add(callable_name)

    def exclude(self, callable_name):
        if callable_name in self.included_:
            self.included_.remove(callable_name)

    def export(self):
        call_tree = ''
        if self.root_.name in self.included_:
            call_tree = ' -> ' + quote(self.root_.participant) + ': ' + self.root_.callable + '\n'
            call_tree += 'activate {}\n'.format(quote(self.root_.participant))
        call_tree += self.export_calls_(parent=self.root_, included_parent_name='')
        if self.root_.name in self.included_:
            call_tree += 'deactivate {}\n'.format(quote(self.root_.participant))
        return '@startuml\n\n{}\n@enduml'.format(call_tree)

    def export_calls_(self, parent, included_parent_name):
        call_tree = ''
        if parent is None:
            return call_tree
        parent_name = included_parent_name
        if parent.name in self.included_:
            parent_name = parent.participant
        for call in self.call_graph_access_.get_calls_of(parent.name):
            if call.name in self.included_:
                call_tree += quote(parent_name) + ' -> ' + quote(call.participant) + ': ' + call.callable + '\n'
                # TODO(KNR): avoid redundant activations
                call_tree += 'activate {}\n'.format(quote(call.participant))
            call_tree += self.export_calls_(parent=call, included_parent_name=parent_name)
            if call.name in self.included_:
                call_tree += 'deactivate {}\n'.format(quote(call.participant))
        return call_tree

    def dump(self, file_name, entry_point, include_system_headers=False, extra_arguments=None):
        tu_access = ClangTUAccess(file_name=file_name, extra_arguments=extra_arguments)
        self.call_graph_access_ = ClangCallGraphAccess(include_system_headers=include_system_headers)
        for file_name, compiler_arguments in tu_access.files.items():
            self.call_graph_access_.parse_tu(tu_file_name=file_name, compiler_arguments=compiler_arguments)
        root = self.call_graph_access_.get_callable(entry_point)
        return self.dump_callable_(root, 0)

    def list_tu_candidates_(self, callable_name):
        callable = self.call_graph_access_.get_callable(callable_name)
        assert callable  # TODO(KNR): be nicer

        search_key = callable.get_spelling()
        tu_candidates = {file_name: compiler_arguments
                         for file_name, compiler_arguments in self.tu_access_.files.items()
                         if find_text_in_file_(file_name=file_name, text=search_key)}
        # TODO(KNR): figure out how to avoid Decorate-Sort-Undecorate idiom
        decorated = [(rate_path_commonality_(callable.used_in_file, file_name), file_name, compiler_arguments)
                     for file_name, compiler_arguments in tu_candidates.items()]
        decorated.sort(reverse=True)
        tu_candidates = {file_name: compiler_arguments for _, file_name, compiler_arguments in decorated}
        return tu_candidates

    def dump_callable_(self, callable, level):
        indentation = level * '  '
        call_tree = '{}{}\n'.format(indentation, callable.name)
        for call in self.call_graph_access_.get_calls_of(callable.name):
            call_tree += self.dump_callable_(call, level + 1)
        return call_tree
コード例 #7
0
def test__given_parsed_tu_with_one_function__get_callables_in_returns_one_callable():
    access = ClangCallGraphAccess()
    with generate_file('one-function.cpp', 'void f() {}') as file_name:
        access.parse_tu(tu_file_name=file_name, compiler_arguments='')
        callables = access.get_callables_in(file_name)
    assert len(callables) == 1
コード例 #8
0
def test__given_parsed_tu_with_one_function__callables_contains_one_item():
    access = ClangCallGraphAccess()
    with generate_file('syntax-error.cpp', 'void f() {}') as file_name:
        access.parse_tu(tu_file_name=file_name, compiler_arguments='')
    callables = access.callables
    assert len(callables) == 1
コード例 #9
0
def test__given_file_with_syntax_error__parse_tu_throws():
    access = ClangCallGraphAccess()
    with generate_file('syntax-error.cpp', 'void f() {') as file_name:
        with pytest.raises(SyntaxError):
            access.parse_tu(tu_file_name=file_name, compiler_arguments='')
コード例 #10
0
def test__given_non_existing_file__parse_tu_throws():
    access = ClangCallGraphAccess()
    with pytest.raises(FileNotFoundError):
        access.parse_tu(tu_file_name='a-file-that-doesnt-exist', compiler_arguments='')