furnace 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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