Class: AppMap::Algorithm::PruneClassMap

Inherits:
Object
  • Object
show all
Defined in:
lib/appmap/algorithm/prune_class_map.rb

Overview

Prune a class map so that only functions, classes and packages which are referenced by some event are retained.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(class_map) ⇒ PruneClassMap

Construct the algorithm, with a class map that will be pruned in place.



14
15
16
17
# File 'lib/appmap/algorithm/prune_class_map.rb', line 14

def initialize(class_map)
  @class_map = class_map
  @logger = ->(msg) {}
end

Instance Attribute Details

#class_mapObject (readonly)

Returns the value of attribute class_map.



8
9
10
# File 'lib/appmap/algorithm/prune_class_map.rb', line 8

def class_map
  @class_map
end

#eventsObject

Returns the value of attribute events.



11
12
13
# File 'lib/appmap/algorithm/prune_class_map.rb', line 11

def events
  @events
end

#logger=(value) ⇒ Object (writeonly)

Set this attribute to a function which will log algorithm events.



10
11
12
# File 'lib/appmap/algorithm/prune_class_map.rb', line 10

def logger=(value)
  @logger = value
end

Instance Method Details

#performObject



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/appmap/algorithm/prune_class_map.rb', line 19

def perform
  # This proc counts the number of objects in the class map whose type is 'k'
  count = proc do |k, e|
    n = 0
    n += 1 if e['type'] == k
    n += (e['children'] || []).map { |child| count.call(k, child) }.reduce(0, :+)
    n
  end

  @logger.call "Full classMap contains #{class_map.map { |m| count.call('class', m) }.reduce(0, :+)} classes"

  # Prune all the classes which fail a test.
  reject = proc do |list, test|
    list.tap do |_|
      list.each do |item|
        children = item['children']
        next unless children

        reject.call(children, test)
      end
      list.reject!(&test)
    end
  end

  if events
    locations = \
      Set.new(events.select { |e| e['event'] == 'call' }
      .map { |e| [ e['path'], e['lineno'] ].join(':') })

    # Prune all functions which aren't called
    reject.call class_map,
                ->(e) { e['type'] == 'function' && !locations.member?(e['location']) }
  end

  # Prune all empty classes
  reject.call class_map,
              ->(e) { e['type'] == 'class' && (e['children'] || []).empty? }

  # Prune all empty packages
  reject.call class_map,
              ->(e) { e['type'] == 'package' && (e['children'] || []).empty? }

  @logger.call "Pruned classMap contains #{class_map.map { |m| count.call('class', m) }.reduce(0, :+)} classes"

  class_map
end