furnace 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/furnace/ast/node.rb +23 -29
- data/lib/furnace/ast/strict_visitor.rb +20 -0
- data/lib/furnace/ast/visitor.rb +0 -2
- data/lib/furnace/ast.rb +1 -1
- data/lib/furnace/cfg/graph.rb +37 -23
- data/lib/furnace/cfg/node.rb +8 -4
- data/lib/furnace/graphviz.rb +2 -2
- data/lib/furnace/transform/iterative_process.rb +26 -0
- data/lib/furnace/transform/pipeline.rb +2 -0
- data/lib/furnace/version.rb +1 -1
- data/lib/furnace.rb +1 -0
- metadata +5 -3
- data/lib/furnace/ast/symbolic_node.rb +0 -34
data/lib/furnace/ast/node.rb
CHANGED
@@ -1,25 +1,11 @@
|
|
1
1
|
module Furnace::AST
|
2
2
|
class Node
|
3
|
-
attr_accessor :type, :
|
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
|
data/lib/furnace/ast/visitor.rb
CHANGED
data/lib/furnace/ast.rb
CHANGED
data/lib/furnace/cfg/graph.rb
CHANGED
@@ -22,28 +22,32 @@ module Furnace::CFG
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def eliminate_unreachable!
|
25
|
-
|
25
|
+
worklist = Set[ entry, exit ]
|
26
26
|
reachable = Set[]
|
27
27
|
|
28
|
-
while
|
29
|
-
node =
|
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
|
-
|
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
|
-
|
41
|
+
worklist.add node.exception
|
41
42
|
end
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
46
|
@nodes.each do |node|
|
46
|
-
|
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.
|
66
|
+
if node.metadata[:keep]
|
63
67
|
next
|
64
68
|
end
|
65
69
|
|
66
|
-
if node.targets.
|
67
|
-
target.sources.
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
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}
|
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, "
|
273
|
+
graph.edge node.label, node.exception_label, "", color: 'orange'
|
260
274
|
end
|
261
275
|
end
|
262
276
|
end
|
data/lib/furnace/cfg/node.rb
CHANGED
@@ -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 @
|
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
|
data/lib/furnace/graphviz.rb
CHANGED
@@ -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
|
data/lib/furnace/version.rb
CHANGED
data/lib/furnace.rb
CHANGED
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.
|
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-
|
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/
|
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
|