rdg 0.0.2 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +2 -2
  5. data/Gemfile +1 -1
  6. data/RELEASES.md +10 -0
  7. data/TODO.md +68 -0
  8. data/bin/ast +5 -4
  9. data/bin/cfg +5 -4
  10. data/lib/rdg/analysis/analyser.rb +30 -0
  11. data/lib/rdg/analysis/composite.rb +23 -0
  12. data/lib/rdg/analysis/context.rb +17 -0
  13. data/lib/rdg/analysis/equivalences.rb +25 -0
  14. data/lib/rdg/analysis/propagater.rb +51 -0
  15. data/lib/rdg/analysis/registry.rb +35 -0
  16. data/lib/rdg/cfg.rb +12 -41
  17. data/lib/rdg/control/begin.rb +6 -4
  18. data/lib/rdg/control/break.rb +19 -0
  19. data/lib/rdg/control/case.rb +25 -0
  20. data/lib/rdg/control/{while.rb → conditional_loop.rb} +8 -7
  21. data/lib/rdg/control/def.rb +21 -7
  22. data/lib/rdg/control/ensure.rb +30 -0
  23. data/lib/rdg/control/for.rb +29 -0
  24. data/lib/rdg/control/handler.rb +25 -0
  25. data/lib/rdg/control/if.rb +7 -6
  26. data/lib/rdg/control/jump.rb +33 -0
  27. data/lib/rdg/control/jump_to_start.rb +11 -0
  28. data/lib/rdg/control/next.rb +9 -0
  29. data/lib/rdg/control/none.rb +5 -3
  30. data/lib/rdg/control/redo.rb +9 -0
  31. data/lib/rdg/control/rescue.rb +31 -0
  32. data/lib/rdg/control/rescue_body.rb +29 -0
  33. data/lib/rdg/control/retry.rb +13 -0
  34. data/lib/rdg/control/return.rb +5 -6
  35. data/lib/rdg/control/when.rb +27 -0
  36. data/lib/rdg/graph/bidirected_adjacency_graph.rb +19 -0
  37. data/lib/rdg/graph/rgl/allow_duplicates.rb +45 -0
  38. data/lib/rdg/tree/ast.rb +15 -4
  39. data/lib/rdg/version.rb +1 -1
  40. data/rdg.gemspec +3 -2
  41. data/spec/integration/cfg/conditionals/case_spec.rb +66 -0
  42. data/spec/integration/cfg/{if_spec.rb → conditionals/if_spec.rb} +24 -9
  43. data/spec/integration/cfg/conditionals/unless_spec.rb +53 -0
  44. data/spec/integration/cfg/exceptions_spec.rb +131 -0
  45. data/spec/integration/cfg/loops/loop_control_spec.rb +90 -0
  46. data/spec/integration/cfg/loops/loop_spec.rb +70 -0
  47. data/spec/integration/cfg/sequence_spec.rb +7 -1
  48. data/spec/support/doubles/fake_ast.rb +15 -0
  49. data/spec/support/matchers/flow_between_matcher.rb +1 -1
  50. data/spec/unit/analysis/composite_spec.rb +47 -0
  51. data/spec/unit/analysis/equivalences_spec.rb +29 -0
  52. data/spec/unit/{control/analyser_spec.rb → analysis/propagater_spec.rb} +23 -12
  53. data/spec/unit/analysis/registry_spec.rb +61 -0
  54. data/spec/unit/control/begin_spec.rb +3 -6
  55. data/spec/unit/control/break_spec.rb +26 -0
  56. data/spec/unit/control/case_spec.rb +66 -0
  57. data/spec/unit/control/conditional_loop_spec.rb +22 -0
  58. data/spec/unit/control/ensure_spec.rb +33 -0
  59. data/spec/unit/control/for_spec.rb +26 -0
  60. data/spec/unit/control/handler_spec.rb +27 -0
  61. data/spec/unit/control/if_spec.rb +26 -18
  62. data/spec/unit/control/jump_spec.rb +43 -0
  63. data/spec/unit/control/jump_to_start_spec.rb +22 -0
  64. data/spec/unit/control/rescue_body_spec.rb +26 -0
  65. data/spec/unit/control/rescue_spec.rb +62 -0
  66. data/spec/unit/control/return_spec.rb +1 -10
  67. data/spec/unit/control/when_spec.rb +59 -0
  68. data/spec/unit/graph/bidirected_adjacency_graph_spec.rb +49 -0
  69. metadata +91 -18
  70. data/lib/rdg/control/analyser.rb +0 -44
  71. data/spec/integration/cfg/methods_spec.rb +0 -39
  72. data/spec/unit/control/def_spec.rb +0 -28
  73. data/spec/unit/control/while_spec.rb +0 -25
@@ -0,0 +1,19 @@
1
+ require_relative "jump"
2
+
3
+ module RDG
4
+ module Control
5
+ class Break < Jump
6
+ register_analyser :break
7
+
8
+ def new_successors
9
+ graph
10
+ .each_successor(test)
11
+ .reject { |s| s.ancestors.include?(block) }
12
+ end
13
+
14
+ def test
15
+ equivalences.first(block.children.first)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ require "rdg/analysis/propagater"
2
+
3
+ module RDG
4
+ module Control
5
+ class Case < Analysis::Propagater
6
+ register_analyser :case
7
+
8
+ def prepare
9
+ @expression, *@consequences = nodes
10
+ end
11
+
12
+ def internal_flow_edges
13
+ nodes.each_cons(2).to_a
14
+ end
15
+
16
+ def start_node
17
+ @expression
18
+ end
19
+
20
+ def end_nodes
21
+ @consequences
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,10 +1,11 @@
1
- require_relative "analyser"
1
+ require "rdg/analysis/propagater"
2
2
 
3
3
  module RDG
4
4
  module Control
5
- class While < Analyser
6
- def initialize(ast_node, graph, state)
7
- super(ast_node, graph, state)
5
+ class ConditionalLoop < Analysis::Propagater
6
+ register_analyser :while, :until
7
+
8
+ def prepare
8
9
  @predicate, @body = children
9
10
  end
10
11
 
@@ -12,12 +13,12 @@ module RDG
12
13
  [[@predicate, @body], [@body, @predicate]]
13
14
  end
14
15
 
15
- def start_nodes
16
- [@predicate]
16
+ def start_node
17
+ @predicate
17
18
  end
18
19
 
19
20
  def end_nodes
20
- [@body]
21
+ [@predicate]
21
22
  end
22
23
  end
23
24
  end
@@ -1,14 +1,28 @@
1
+ require "rdg/analysis/propagater"
2
+
1
3
  module RDG
2
4
  module Control
3
- class Def
4
- def initialize(ast_node, graph, state)
5
- @graph, @state = graph, state
6
- @name, @args, @body = ast_node.children
5
+ class Def < Analysis::Propagater
6
+ register_analyser :def
7
+
8
+ def prepare
9
+ _name, _args, @body = children
10
+ end
11
+
12
+ def internal_flow_edges
13
+ []
14
+ end
15
+
16
+ def start_node
17
+ @body
18
+ end
19
+
20
+ def end_nodes
21
+ [@body]
7
22
  end
8
23
 
9
- def analyse
10
- @state[:current_method] = @name
11
- @graph.add_edge(@name, @body)
24
+ def nodes
25
+ [@body]
12
26
  end
13
27
  end
14
28
  end
@@ -0,0 +1,30 @@
1
+ require "rdg/analysis/propagater"
2
+
3
+ module RDG
4
+ module Control
5
+ class Ensure < Analysis::Propagater
6
+ register_analyser :ensure
7
+
8
+ def prepare
9
+ @body, @finaliser = nodes
10
+ end
11
+
12
+ def analyse
13
+ super
14
+ registry.prepend_for(@finaliser, Handler)
15
+ end
16
+
17
+ def internal_flow_edges
18
+ [[@body, @finaliser]]
19
+ end
20
+
21
+ def start_node
22
+ @body
23
+ end
24
+
25
+ def end_nodes
26
+ [@finaliser]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require "rdg/analysis/propagater"
2
+
3
+ module RDG
4
+ module Control
5
+ class For < Analysis::Propagater
6
+ register_analyser :for
7
+
8
+ def prepare
9
+ _, @iterable, @body = children
10
+ end
11
+
12
+ def internal_flow_edges
13
+ [[@iterable, @body], [@body, @iterable]]
14
+ end
15
+
16
+ def start_node
17
+ @iterable
18
+ end
19
+
20
+ def end_nodes
21
+ [@iterable]
22
+ end
23
+
24
+ def nodes
25
+ [@iterable, @body]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ require "rdg/analysis/analyser"
2
+
3
+ module RDG
4
+ module Control
5
+ class Handler < Analysis::Analyser
6
+ def analyse
7
+ add_an_edge_from_every_main_node_to_the_handler
8
+ end
9
+
10
+ private
11
+
12
+ def add_an_edge_from_every_main_node_to_the_handler
13
+ main.each { |m| graph.add_edge(m, @ast_node) }
14
+ end
15
+
16
+ def main
17
+ equivalences.all(block.children.first)
18
+ end
19
+
20
+ def block
21
+ @ast_node.parent
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,10 +1,11 @@
1
- require_relative "analyser"
1
+ require "rdg/analysis/propagater"
2
2
 
3
3
  module RDG
4
4
  module Control
5
- class If < Analyser
6
- def initialize(ast_node, graph, state)
7
- super(ast_node, graph, state)
5
+ class If < Analysis::Propagater
6
+ register_analyser :if
7
+
8
+ def prepare
8
9
  @predicate, *@consequences = children.reject(&:empty?)
9
10
  end
10
11
 
@@ -12,8 +13,8 @@ module RDG
12
13
  @consequences.map { |consequence| [@predicate, consequence] }
13
14
  end
14
15
 
15
- def start_nodes
16
- [@predicate]
16
+ def start_node
17
+ @predicate
17
18
  end
18
19
 
19
20
  def end_nodes
@@ -0,0 +1,33 @@
1
+ require "rdg/analysis/analyser"
2
+
3
+ module RDG
4
+ module Control
5
+ class Jump < Analysis::Analyser
6
+ def analyse
7
+ return unless block
8
+ remove_all_successors
9
+ add_new_successors
10
+ end
11
+
12
+ protected
13
+
14
+ def block_types
15
+ %i(while until for)
16
+ end
17
+
18
+ private
19
+
20
+ def remove_all_successors
21
+ graph.each_successor(@ast_node) { |s| graph.remove_edge(@ast_node, s) }
22
+ end
23
+
24
+ def add_new_successors
25
+ new_successors.each { |s| graph.add_edge(@ast_node, s) }
26
+ end
27
+
28
+ def block
29
+ @ast_node.ancestors.detect { |a| block_types.include?(a.type) }
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "jump"
2
+
3
+ module RDG
4
+ module Control
5
+ class JumpToStart < Jump
6
+ def new_successors
7
+ [equivalences.first(block.children.first)]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "jump_to_start"
2
+
3
+ module RDG
4
+ module Control
5
+ class Next < JumpToStart
6
+ register_analyser :next
7
+ end
8
+ end
9
+ end
@@ -1,10 +1,12 @@
1
+ require "rdg/analysis/analyser"
2
+
1
3
  module RDG
2
4
  module Control
3
- class None
4
- def initialize(_ast_node, _graph, _state)
5
- end
5
+ class None < Analysis::Analyser
6
+ register_default_analyser
6
7
 
7
8
  def analyse
9
+ # do nothing
8
10
  end
9
11
  end
10
12
  end
@@ -0,0 +1,9 @@
1
+ require_relative "jump_to_start"
2
+
3
+ module RDG
4
+ module Control
5
+ class Redo < JumpToStart
6
+ register_analyser :redo
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ require "rdg/analysis/propagater"
2
+ require_relative "handler"
3
+
4
+ module RDG
5
+ module Control
6
+ class Rescue < Analysis::Propagater
7
+ register_analyser :rescue
8
+
9
+ def prepare
10
+ @main, *@handlers, @alternative = children
11
+ end
12
+
13
+ def analyse
14
+ super
15
+ @handlers.each { |h| registry.prepend_for(h, Handler) }
16
+ end
17
+
18
+ def internal_flow_edges
19
+ @alternative.empty? ? [] : [[@main, @alternative]]
20
+ end
21
+
22
+ def start_node
23
+ @main
24
+ end
25
+
26
+ def end_nodes
27
+ @alternative.empty? ? nodes : @handlers.push(@alternative)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ require "rdg/analysis/propagater"
2
+
3
+ module RDG
4
+ module Control
5
+ class RescueBody < Analysis::Propagater
6
+ register_analyser :resbody
7
+
8
+ def prepare
9
+ _exception_types, _variable_name, *@statements = children
10
+ end
11
+
12
+ def internal_flow_edges
13
+ @statements.each_cons(2).to_a
14
+ end
15
+
16
+ def start_node
17
+ @statements.first
18
+ end
19
+
20
+ def end_nodes
21
+ @statements.last(1)
22
+ end
23
+
24
+ def nodes
25
+ @statements
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "jump_to_start"
2
+
3
+ module RDG
4
+ module Control
5
+ class Retry < JumpToStart
6
+ register_analyser :retry
7
+
8
+ def block_types
9
+ %i(rescue)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,13 +1,12 @@
1
+ require "rdg/analysis/analyser"
2
+
1
3
  module RDG
2
4
  module Control
3
- class Return
4
- def initialize(ast_node, graph, state)
5
- @graph, @ast_node, @state = graph, ast_node, state
6
- end
5
+ class Return < Analysis::Analyser
6
+ register_analyser :return
7
7
 
8
8
  def analyse
9
- return unless @state.key?(:current_method)
10
- @graph.each_successor(@ast_node) { |s| @graph.remove_edge(@ast_node, s) }
9
+ graph.each_successor(@ast_node) { |s| graph.remove_edge(@ast_node, s) }
11
10
  end
12
11
  end
13
12
  end
@@ -0,0 +1,27 @@
1
+ require "rdg/analysis/propagater"
2
+
3
+ module RDG
4
+ module Control
5
+ class When < Analysis::Propagater
6
+ register_analyser :when
7
+
8
+ def prepare
9
+ @test, @action = children
10
+ end
11
+
12
+ def internal_flow_edges
13
+ [[@test, @action]]
14
+ end
15
+
16
+ def start_node
17
+ @test
18
+ end
19
+
20
+ def propogate_outgoing_flow
21
+ successors = graph.each_successor(@ast_node).to_a
22
+ graph.add_edge(@test, successors.first)
23
+ graph.add_edge(@action, successors.last)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ require "rgl/adjacency"
2
+ require "rgl/dot"
3
+ require_relative "rgl/allow_duplicates"
4
+
5
+ module RDG
6
+ module Graph
7
+ class BidirectedAdjacencyGraph < ::RGL::DirectedAdjacencyGraph
8
+ include RGL::AllowDuplicates
9
+
10
+ def each_predecessor(vertex, &block)
11
+ each_vertex.select { |v| each_adjacent(v).include?(vertex) }.each(&block)
12
+ end
13
+
14
+ def each_successor(vertex, &block)
15
+ each_adjacent(vertex, &block) if has_vertex?(vertex)
16
+ end
17
+ end
18
+ end
19
+ end