furnace 0.2.5 → 0.2.6

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.
@@ -1,25 +1,11 @@
1
1
  module Furnace::AST
2
2
  class Node
3
- attr_accessor :type, :parent, :children, :metadata
3
+ attr_accessor :type, :children, :metadata
4
4
 
5
5
  def initialize(type, children=[], metadata={})
6
6
  @type, @children, @metadata = type.to_sym, children, metadata
7
7
  end
8
8
 
9
- def normalize_hierarchy!
10
- @children.each do |child|
11
- if child.respond_to? :parent=
12
- child.parent = self
13
- end
14
-
15
- if child.respond_to? :normalize_hierarchy!
16
- child.normalize_hierarchy!
17
- end
18
- end
19
-
20
- self
21
- end
22
-
23
9
  def update(type, children=nil, metadata={})
24
10
  @type = type
25
11
  @children = children || @children
@@ -52,18 +38,6 @@ module Furnace::AST
52
38
  end
53
39
  end
54
40
 
55
- def index
56
- parent.children.find_index(self)
57
- end
58
-
59
- def next
60
- parent.children[index + 1]
61
- end
62
-
63
- def prev
64
- parent.children[index - 1]
65
- end
66
-
67
41
  def to_s
68
42
  "(#{fancy_type} ...)"
69
43
  end
@@ -71,6 +45,12 @@ module Furnace::AST
71
45
  def to_sexp(indent=0)
72
46
  str = "#{" " * indent}(#{fancy_type}"
73
47
 
48
+ if @metadata[:ellipsis]
49
+ str << " <omitted>)"
50
+
51
+ return str
52
+ end
53
+
74
54
  children.each do |child|
75
55
  if (!children[0].is_a?(Node) && child.is_a?(Node)) ||
76
56
  (children[0].is_a?(Node) && child.is_a?(Node) &&
@@ -96,10 +76,24 @@ module Furnace::AST
96
76
 
97
77
  def fancy_type
98
78
  dasherized = @type.to_s.gsub('_', '-')
79
+
80
+ if @metadata.any?
81
+ metainfo = @metadata.dup
82
+ metainfo.delete :label
83
+ metainfo.delete :origin
84
+ metainfo.delete :ellipsis
85
+
86
+ if metainfo.any?
87
+ metainfo = "#{metainfo.inspect}:"
88
+ else
89
+ metainfo = nil
90
+ end
91
+ end
92
+
99
93
  if @metadata[:label]
100
- "#{@metadata[:label]}:#{dasherized}"
94
+ "#{@metadata[:label]}:#{metainfo}#{dasherized}"
101
95
  else
102
- dasherized
96
+ "#{metainfo}#{dasherized}"
103
97
  end
104
98
  end
105
99
  end
@@ -0,0 +1,20 @@
1
+ module Furnace::AST
2
+ module StrictVisitor
3
+ def visit(node)
4
+ # Invoke a specific handler
5
+ on_handler = :"on_#{node.type}"
6
+ if respond_to? on_handler
7
+ new_node = send on_handler, node
8
+ node = new_node if new_node
9
+ end
10
+
11
+ node
12
+ end
13
+
14
+ def visit_all(nodes)
15
+ nodes.map do |node|
16
+ visit node
17
+ end
18
+ end
19
+ end
20
+ end
@@ -29,8 +29,6 @@ module Furnace::AST
29
29
 
30
30
  node.children.delete_if do |child|
31
31
  if child.is_a? Node
32
- child.parent = node
33
-
34
32
  child.type == :remove
35
33
  end
36
34
  end
data/lib/furnace/ast.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  require "furnace"
2
2
 
3
3
  require "furnace/ast/node"
4
- require "furnace/ast/symbolic_node"
5
4
  require "furnace/ast/visitor"
5
+ require "furnace/ast/strict_visitor"
6
6
 
7
7
  require "furnace/ast/matcher/special"
8
8
  require "furnace/ast/matcher/dsl"
@@ -22,28 +22,32 @@ module Furnace::CFG
22
22
  end
23
23
 
24
24
  def eliminate_unreachable!
25
- queue = [entry]
25
+ worklist = Set[ entry, exit ]
26
26
  reachable = Set[]
27
27
 
28
- while queue.any?
29
- node = queue.shift
28
+ while worklist.any?
29
+ node = worklist.first
30
+ worklist.delete node
30
31
  reachable.add node
31
32
 
32
33
  node.targets.each do |target|
33
34
  unless reachable.include? target
34
- queue.push target
35
+ worklist.add target
35
36
  end
36
37
  end
37
38
 
38
39
  if node.exception
39
40
  unless reachable.include? node.exception
40
- queue.push node.exception
41
+ worklist.add node.exception
41
42
  end
42
43
  end
43
44
  end
44
45
 
45
46
  @nodes.each do |node|
46
- @nodes.delete node unless reachable.include? node
47
+ unless reachable.include? node
48
+ @nodes.delete node
49
+ yield node if block_given?
50
+ end
47
51
  end
48
52
 
49
53
  flush
@@ -59,42 +63,41 @@ module Furnace::CFG
59
63
  next if target == @exit
60
64
 
61
65
  # Skip explicitly non-redundant nodes
62
- if node.cti && node.cti.metadata[:keep]
66
+ if node.metadata[:keep]
63
67
  next
64
68
  end
65
69
 
66
- if node.targets.count == 1 &&
67
- target.sources.count == 1 &&
70
+ if node.targets.uniq == [target] &&
71
+ target.sources.uniq == [node] &&
68
72
  node.exception == target.exception
69
73
 
74
+ yield node, target if block_given?
75
+
70
76
  node.insns.delete node.cti
71
- @nodes.delete node
72
77
  @nodes.delete target
78
+ worklist.delete target
73
79
 
74
- new_node = Node.new(self,
75
- node.label,
76
- node.insns + target.insns,
77
- target.cti,
78
- target.target_labels,
79
- target.exception_label)
80
- @nodes.add new_node
81
- worklist.add new_node
80
+ node.insns.concat target.insns
81
+ node.cti = target.cti
82
+ node.target_labels = target.target_labels
82
83
 
83
- if @entry == node
84
- @entry = new_node
85
- end
84
+ worklist.add node
86
85
 
87
86
  flush
88
87
  elsif node.targets.count == 1 &&
89
88
  node.insns.empty?
89
+
90
90
  target = node.targets.first
91
91
 
92
+ yield target, node if block_given?
93
+
92
94
  node.sources.each do |source|
93
95
  index = source.targets.index(node)
94
96
  source.target_labels[index] = target.label
95
97
  end
96
98
 
97
99
  @nodes.delete node
100
+ worklist.delete node
98
101
 
99
102
  if @entry == node
100
103
  @entry = target
@@ -231,6 +234,9 @@ module Furnace::CFG
231
234
  def flush
232
235
  @source_map = nil
233
236
  @label_map.clear
237
+
238
+ @dominators = nil
239
+ @postdominators = nil
234
240
  end
235
241
 
236
242
  def to_graphviz
@@ -239,7 +245,15 @@ module Furnace::CFG
239
245
  if node.label == nil
240
246
  contents = "<exit>"
241
247
  else
242
- contents = "<#{node.label.inspect}>\n#{node.insns.map(&:inspect).join("\n")}"
248
+ contents = "<#{node.label.inspect}>"
249
+ end
250
+
251
+ if node.metadata.any?
252
+ contents << "\n#{node.metadata.inspect}"
253
+ end
254
+
255
+ if node.insns.any?
256
+ contents << "\n#{node.insns.map(&:inspect).join("\n")}"
243
257
  end
244
258
 
245
259
  options = {}
@@ -256,7 +270,7 @@ module Furnace::CFG
256
270
  end
257
271
 
258
272
  if node.exception_label
259
- graph.edge node.label, node.exception_label, "Exc", color: 'orange'
273
+ graph.edge node.label, node.exception_label, "", color: 'orange'
260
274
  end
261
275
  end
262
276
  end
@@ -5,14 +5,18 @@ module Furnace::CFG
5
5
  attr_accessor :target_labels, :exception_label
6
6
  attr_accessor :instructions, :control_transfer_instruction
7
7
 
8
+ attr_accessor :metadata
9
+
8
10
  alias :insns :instructions
9
11
  alias :insns= :instructions=
10
12
  alias :cti :control_transfer_instruction
11
13
  alias :cti= :control_transfer_instruction=
12
14
 
13
15
  def initialize(cfg, label=nil, insns=[], cti=nil,
14
- target_labels=[], exception_label=nil)
16
+ target_labels=[], exception_label=nil,
17
+ metadata={})
15
18
  @cfg, @label = cfg, label
19
+ @metadata = metadata
16
20
 
17
21
  @instructions = insns
18
22
  @control_transfer_instruction = cti
@@ -64,10 +68,10 @@ module Furnace::CFG
64
68
  "<#{@label}:#{@instructions.join ", "}>"
65
69
  elsif @label
66
70
  "<#{@label}>"
67
- elsif @insns
68
- "<!unlabeled>"
71
+ elsif @instructions
72
+ "<!unlabeled:#{@instructions.join ", "}>"
69
73
  else
70
- "<!exit>"
74
+ "<!exit#{metadata}>"
71
75
  end
72
76
  end
73
77
  end
@@ -29,14 +29,14 @@ class Furnace::Graphviz
29
29
  label: label
30
30
  })
31
31
 
32
- @code << %Q{#{name.inspect} #{graphviz_options(options)};\n}
32
+ @code << %Q{"#{name.inspect}" #{graphviz_options(options)};\n}
33
33
  end
34
34
 
35
35
  def edge(from, to, label="", options={})
36
36
  options = options.merge({
37
37
  label: label.inspect
38
38
  })
39
- @code << %Q{#{from.inspect} -> #{to.inspect} #{graphviz_options(options)};\n}
39
+ @code << %Q{"#{from.inspect}" -> "#{to.inspect}" #{graphviz_options(options)};\n}
40
40
  end
41
41
 
42
42
  def to_s
@@ -0,0 +1,26 @@
1
+ module Furnace
2
+ module Transform
3
+ class IterativeProcess
4
+ def initialize(stages)
5
+ @stages = stages
6
+ end
7
+
8
+ def transform(*sequence)
9
+ loop do
10
+ changed = false
11
+
12
+ @stages.each do |stage|
13
+ return sequence if stage.nil?
14
+
15
+ if new_sequence = stage.transform(*sequence)
16
+ changed = true
17
+ sequence = new_sequence
18
+ end
19
+ end
20
+
21
+ return sequence unless changed
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -7,6 +7,8 @@ module Furnace
7
7
 
8
8
  def run(*sequence)
9
9
  @stages.each do |stage|
10
+ break if stage.nil?
11
+
10
12
  sequence = stage.transform *sequence
11
13
  end
12
14
 
@@ -1,3 +1,3 @@
1
1
  module Furnace
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  end
data/lib/furnace.rb CHANGED
@@ -8,5 +8,6 @@ require "furnace/cfg"
8
8
  require "furnace/code"
9
9
 
10
10
  require "furnace/transform/pipeline"
11
+ require "furnace/transform/iterative_process"
11
12
 
12
13
  require "furnace/graphviz"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: furnace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-04 00:00:00.000000000 Z
12
+ date: 2012-09-30 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Furnace is a static code analysis framework for dynamic languages, aimed
15
15
  at efficient type and behavior inference.
@@ -30,7 +30,7 @@ files:
30
30
  - lib/furnace/ast/matcher/dsl.rb
31
31
  - lib/furnace/ast/matcher/special.rb
32
32
  - lib/furnace/ast/node.rb
33
- - lib/furnace/ast/symbolic_node.rb
33
+ - lib/furnace/ast/strict_visitor.rb
34
34
  - lib/furnace/ast/visitor.rb
35
35
  - lib/furnace/cfg.rb
36
36
  - lib/furnace/cfg/graph.rb
@@ -43,6 +43,7 @@ files:
43
43
  - lib/furnace/code/terminal_token.rb
44
44
  - lib/furnace/code/token.rb
45
45
  - lib/furnace/graphviz.rb
46
+ - lib/furnace/transform/iterative_process.rb
46
47
  - lib/furnace/transform/pipeline.rb
47
48
  - lib/furnace/version.rb
48
49
  homepage: http://github.com/whitequark/furnace
@@ -70,3 +71,4 @@ signing_key:
70
71
  specification_version: 3
71
72
  summary: A static code analysis framework
72
73
  test_files: []
74
+ has_rdoc:
@@ -1,34 +0,0 @@
1
- module Furnace::AST
2
- class SymbolicNode
3
- def initialize(name)
4
- @name = name.to_sym
5
- end
6
-
7
- def to_sym
8
- @name
9
- end
10
-
11
- def ===(name)
12
- @name == name.to_sym
13
- end
14
-
15
- def inspect
16
- @name.to_s
17
- end
18
- end
19
-
20
- class MethodName < SymbolicNode
21
- def inspect
22
- ".#{@name}"
23
- end
24
- end
25
-
26
- class LocalVariable < SymbolicNode
27
- def inspect
28
- "%#{@name}"
29
- end
30
- end
31
-
32
- class Constant < SymbolicNode
33
- end
34
- end