furnace 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -184,6 +184,8 @@ module Furnace::AST
184
184
 
185
185
  if pattern === astlet
186
186
  true
187
+ elsif pattern.is_a? MatcherSpecial
188
+ false
187
189
  elsif astlet.is_a? Node
188
190
  submatch([astlet.type].concat(astlet.children), pattern, captures)
189
191
  elsif astlet.is_a? Array
data/lib/furnace/ast.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "furnace/base"
1
+ require "furnace"
2
2
 
3
3
  require "furnace/ast/node"
4
4
  require "furnace/ast/symbolic_node"
@@ -58,6 +58,11 @@ module Furnace::CFG
58
58
  target = node.targets[0]
59
59
  next if target == @exit
60
60
 
61
+ # Skip explicitly non-redundant nodes
62
+ if node.cti && node.cti.metadata[:keep]
63
+ next
64
+ end
65
+
61
66
  if node.targets.count == 1 &&
62
67
  target.sources.count == 1 &&
63
68
  node.exception == target.exception
data/lib/furnace/cfg.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require "set"
2
2
 
3
- require "furnace/base"
3
+ require "furnace"
4
4
 
5
5
  require "furnace/cfg/graph"
6
6
  require "furnace/cfg/node"
data/lib/furnace/code.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require "set"
2
2
 
3
- require "furnace/base"
3
+ require "furnace"
4
4
 
5
5
  require "furnace/code/token"
6
6
  require "furnace/code/terminal_token"
@@ -1,4 +1,4 @@
1
- require 'furnace/base'
1
+ require 'furnace'
2
2
 
3
3
  class Furnace::Graphviz
4
4
  def initialize
@@ -1,3 +1,3 @@
1
1
  module Furnace
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/furnace.rb CHANGED
@@ -1,21 +1,12 @@
1
+ module Furnace
2
+ end
3
+
1
4
  require "furnace/version"
2
5
 
3
6
  require "furnace/ast"
4
7
  require "furnace/cfg"
5
8
  require "furnace/code"
6
9
 
7
- require "furnace/anf/node"
8
- require "furnace/anf/edge"
9
- require "furnace/anf/let_node"
10
- require "furnace/anf/in_node"
11
- require "furnace/anf/if_node"
12
- require "furnace/anf/return_node"
13
- require "furnace/anf/graph"
14
-
15
- require "furnace/transform"
16
-
17
- require "furnace/graphviz"
10
+ require "furnace/transform/pipeline"
18
11
 
19
- if RUBY_ENGINE != "rbx"
20
- raise "Sorry, Furnace only works on Rubinius."
21
- end
12
+ 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.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,23 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-06 00:00:00.000000000 Z
12
+ date: 2012-06-10 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.
16
16
  email:
17
17
  - whitequark@whitequark.org
18
- executables:
19
- - furnace
18
+ executables: []
20
19
  extensions: []
21
20
  extra_rdoc_files: []
22
21
  files:
23
22
  - .gitignore
24
- - .rvmrc
25
23
  - Gemfile
26
24
  - LICENSE
27
25
  - Rakefile
28
- - bin/furnace
29
26
  - furnace.gemspec
30
27
  - lib/furnace.rb
31
28
  - lib/furnace/ast.rb
@@ -35,7 +32,6 @@ files:
35
32
  - lib/furnace/ast/node.rb
36
33
  - lib/furnace/ast/symbolic_node.rb
37
34
  - lib/furnace/ast/visitor.rb
38
- - lib/furnace/base.rb
39
35
  - lib/furnace/cfg.rb
40
36
  - lib/furnace/cfg/graph.rb
41
37
  - lib/furnace/cfg/node.rb
@@ -47,14 +43,7 @@ files:
47
43
  - lib/furnace/code/terminal_token.rb
48
44
  - lib/furnace/code/token.rb
49
45
  - lib/furnace/graphviz.rb
50
- - lib/furnace/transform.rb
51
- - lib/furnace/transform/generic/cfg_build.rb
52
- - lib/furnace/transform/generic/cfg_normalize.rb
53
- - lib/furnace/transform/generic/label_normalize.rb
54
- - lib/furnace/transform/optimizing/fold_constants.rb
55
46
  - lib/furnace/transform/pipeline.rb
56
- - lib/furnace/transform/rubinius/ast_build.rb
57
- - lib/furnace/transform/rubinius/ast_normalize.rb
58
47
  - lib/furnace/version.rb
59
48
  homepage: http://github.com/whitequark/furnace
60
49
  licenses: []
@@ -81,3 +70,4 @@ signing_key:
81
70
  specification_version: 3
82
71
  summary: A static code analysis framework
83
72
  test_files: []
73
+ has_rdoc:
data/.rvmrc DELETED
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
- # development environment upon cd'ing into the directory
5
-
6
- # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
7
- environment_id="rbx-head@furnace"
8
-
9
- #
10
- # Uncomment following line if you want options to be set only for given project.
11
- #
12
- # PROJECT_JRUBY_OPTS=( --1.9 )
13
-
14
- #
15
- # First we attempt to load the desired environment directly from the environment
16
- # file. This is very fast and efficient compared to running through the entire
17
- # CLI and selector. If you want feedback on which environment was used then
18
- # insert the word 'use' after --create as this triggers verbose mode.
19
- #
20
- if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
21
- && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
22
- then
23
- \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
24
-
25
- if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
26
- then
27
- . "${rvm_path:-$HOME/.rvm}/hooks/after_use"
28
- fi
29
- else
30
- # If the environment file has not yet been created, use the RVM CLI to select.
31
- if ! rvm --create "$environment_id"
32
- then
33
- echo "Failed to create RVM environment '${environment_id}'."
34
- return 1
35
- fi
36
- fi
37
-
38
- #
39
- # If you use an RVM gemset file to install a list of gems (*.gems), you can have
40
- # it be automatically loaded. Uncomment the following and adjust the filename if
41
- # necessary.
42
- #
43
- # filename=".gems"
44
- # if [[ -s "$filename" ]]
45
- # then
46
- # rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
47
- # fi
48
-
49
- # If you use bundler, this might be useful to you:
50
- # if command -v bundle && [[ -s Gemfile ]]
51
- # then
52
- # bundle install
53
- # fi
54
-
55
- if [[ $- == *i* ]] # check for interactive shells
56
- then
57
- echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
58
- else
59
- echo "Using: $GEM_HOME" # don't use colors in interactive shells
60
- fi
61
-
data/bin/furnace DELETED
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'furnace'
4
-
5
- load ARGV[0]
6
-
7
- pipeline = Furnace::Transform::Pipeline.new(*[
8
- Furnace::Transform::Rubinius::ASTBuild.new,
9
- Furnace::Transform::Rubinius::ASTNormalize.new,
10
-
11
- Furnace::Transform::Generic::LabelNormalize.new,
12
- Furnace::Transform::Generic::CFGBuild.new,
13
- Furnace::Transform::Generic::CFGNormalize.new,
14
-
15
- Furnace::Transform::Generic::ANFBuild.new,
16
-
17
- Furnace::Transform::Optimizing::FoldConstants.new,
18
- ])
19
-
20
- cfg, = pipeline.run(method(:main).executable)
21
-
22
- puts cfg.to_graphviz
data/lib/furnace/base.rb DELETED
@@ -1,5 +0,0 @@
1
- module Furnace
2
- end
3
-
4
- require 'furnace/version'
5
-
@@ -1,89 +0,0 @@
1
- module Furnace
2
- module Transform
3
- module Generic
4
- class VariableTracer
5
- include AST::Visitor
6
-
7
- attr_reader :read_set, :write_set
8
-
9
- def initialize
10
- @read_set = Set.new
11
- @write_set = Set.new
12
- @conflict = false
13
- end
14
-
15
- def reset
16
- @read_set.clear
17
- @write_set.clear
18
- @conflict = false
19
- end
20
-
21
- def conflict?
22
- @conflict
23
- end
24
-
25
- def on_set_lvar(node)
26
- var = node.children.first
27
-
28
- @write_set.add var
29
- @conflict ||= @read_set.include?(var)
30
- end
31
-
32
- def on_get_lvar(node)
33
- var = node.children.first
34
-
35
- @read_set.add var
36
- @conflict ||= @write_set.include?(var)
37
- end
38
- end
39
-
40
- class CFGBuild
41
- def transform(ast, target_map, method)
42
- cfg = CFG::Graph.new
43
-
44
- tracer = VariableTracer.new
45
-
46
- ast.children.each do |child|
47
- label = child.metadata[:label]
48
-
49
- # Transfer control to the next operation directly if this
50
- # is a jump target.
51
- if target_map.include? label
52
- cfg.transfer({ nil => label })
53
- end
54
-
55
- # Our CFG must also be easily convertible to ANF/SSA.
56
- # Split blocks if a non-SSA variable operation is encountered.
57
- tracer.visit child
58
-
59
- if tracer.conflict?
60
- cfg.transfer({ nil => child.metadata[:label] })
61
- end
62
-
63
- # Expand current operation.
64
- cfg.expand label, child
65
-
66
- # Transfer control non-sequentaly if needed.
67
- if child.type == :jump
68
- cfg.transfer({ label => child.children[0] })
69
- elsif child.type == :jump_if
70
- cfg.transfer({ label => child.children[0],
71
- nil => child.next.metadata[:label] })
72
- elsif child.type == :return
73
- cfg.transfer({ })
74
- elsif tracer.conflict?
75
- # Reset tracer below.
76
- else
77
- next
78
- end
79
-
80
- # There was a conflict or a control transfer. Reset the tracer.
81
- tracer.reset
82
- end
83
-
84
- [ cfg, method ]
85
- end
86
- end
87
- end
88
- end
89
- end
@@ -1,41 +0,0 @@
1
- module Furnace
2
- module Transform
3
- module Generic
4
- class CFGNormalize
5
- def transform(cfg, method)
6
- cfg.nodes.each do |node|
7
- # If a last operation is an unconditional jump, optimize it out.
8
- last = node.operations.last
9
- if last.type == :jump
10
- edge = node.leaving_edge(last.metadata[:label])
11
-
12
- node.operations.delete last
13
-
14
- cfg.edges.delete edge
15
- cfg.edges.add CFG::Edge.new(cfg, nil, node.label, edge.target.label)
16
- end
17
-
18
- # Remove no-ops.
19
- node.operations.delete_if { |op| op.type == :nop }
20
- end
21
-
22
- # Remove empty nodes.
23
- cfg.nodes.delete_if do |node|
24
- if node.operations.empty?
25
- node.entering_edges.each do |edge|
26
- edge.target = node.default_leaving_edge.target
27
- end
28
- cfg.edges.subtract node.leaving_edges
29
-
30
- true
31
- else
32
- false
33
- end
34
- end
35
-
36
- [ cfg, method ]
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,60 +0,0 @@
1
- module Furnace
2
- module Transform
3
- module Generic
4
- class LabelNormalize
5
- include AST::Visitor
6
-
7
- def transform(ast, method)
8
- # Find the minimal label in each operation sub-tree.
9
- # It's the operation entry point.
10
- visit ast
11
-
12
- # Traverse operations in reverse order and bypass all jump_target's,
13
- # recording the forwarded address in label_map.
14
- label_map = {}
15
-
16
- last_real_operation = nil
17
- ast.children.reverse.each do |child|
18
- if child.type == :jump_target
19
- label = last_real_operation
20
- else
21
- label = child.metadata[:label]
22
- last_real_operation = label
23
- end
24
-
25
- label_map[child.metadata[:label]] = label
26
- end
27
-
28
- # Remove jump_target's.
29
- ast.children.reject! { |c| c.type == :jump_target }
30
-
31
- # Find all jumpable labels and substitute the addresses to forward
32
- # through jump_target's.
33
- target_map = []
34
-
35
- ast.children.each do |child|
36
- if child.type == :jump || child.type == :jump_if
37
- forwarded_target = label_map[child.children[0]]
38
- child.children[0] = forwarded_target
39
-
40
- target_map << forwarded_target
41
- end
42
- end
43
-
44
- [ ast, target_map, method ]
45
- end
46
-
47
- def on_any(node)
48
- return if node.type == :root
49
-
50
- child_nodes = node.children.select { |c| c.is_a? AST::Node }
51
-
52
- new_label = child_nodes.map { |c| c.metadata[:label] }.compact.min
53
- node.metadata[:label] = new_label if new_label
54
-
55
- child_nodes.each { |c| c.metadata.delete :label }
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,19 +0,0 @@
1
- module Furnace
2
- module Transform
3
- module Optimizing
4
- class FoldConstants
5
- def transform(anf, method)
6
- anf.nodes.each do |node|
7
- if node.is_a? ANF::LetNode
8
- node.try_propagate
9
- node.try_eliminate
10
- end
11
- end
12
- anf.eliminate_dead_code
13
-
14
- [ anf, method ]
15
- end
16
- end
17
- end
18
- end
19
- end
@@ -1,53 +0,0 @@
1
- module Furnace
2
- module Transform
3
- module Rubinius
4
- class ASTBuild
5
- def transform(method)
6
- stack = []
7
- map = {}
8
- serial = 0
9
-
10
- ast = AST::Node.new(:root)
11
-
12
- method.decode.each do |opcode|
13
- ins = opcode.instruction
14
-
15
- node = AST::Node.new("rbx_#{ins.opcode}")
16
- node.metadata[:label] = opcode.ip
17
- node.children += opcode.args
18
-
19
- # Compute the real value of consumed values.
20
- case ins.stack_consumed
21
- when Fixnum
22
- consumed = ins.stack_consumed
23
- when Array
24
- #p ins.stack_consumed, opcode.args
25
- consumed = ins.stack_consumed[0] + opcode.args.last
26
- end
27
-
28
- # Pop consumed values and attach to current node.
29
- consumed.times.map { map[stack.pop] }.reverse.each do |child|
30
- child.parent = node
31
- node.children << child
32
- end
33
-
34
- # Push back and map the results.
35
- if ins.stack_produced == 0 || ins.opcode == :ret
36
- node.parent = ast
37
- ast.children << node
38
- elsif ins.stack_produced == 1
39
- map[serial] = node
40
- stack.push serial
41
-
42
- serial += 1
43
- else
44
- raise RuntimeError, "don't know what to do with opcode #{opcode.inspect}"
45
- end
46
- end
47
-
48
- [ ast, method ]
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,170 +0,0 @@
1
- module Furnace
2
- module Transform
3
- module Rubinius
4
- class ASTNormalize
5
- include AST::Visitor
6
-
7
- def transform(ast, method)
8
- @locals = method.local_names
9
- @literals = method.literals
10
-
11
- visit ast
12
-
13
- [ ast, method ]
14
- end
15
-
16
- # (rbx-meta-push-0) -> 0
17
- def on_rbx_meta_push_0(node)
18
- node.update(:fixnum, [0], :constant => true)
19
- end
20
-
21
- # (rbx-meta-push-1) -> 1
22
- def on_rbx_meta_push_1(node)
23
- node.update(:fixnum, [1], :constant => true)
24
- end
25
-
26
- # (rbx-meta-push-2) -> 2
27
- def on_rbx_meta_push_2(node)
28
- node.update(:fixnum, [2], :constant => true)
29
- end
30
-
31
- # (rbx-push-int f) -> f
32
- def on_rbx_push_int(node)
33
- node.update(:fixnum, nil, :constant => true)
34
- end
35
-
36
- # (rbx-push-literal l) -> l
37
- def on_rbx_push_literal(node)
38
- node.update(:literal, nil, :constant => true)
39
- end
40
-
41
- # (rbx-push-nil) -> (nil)
42
- def on_rbx_push_nil(node)
43
- node.update(:nil, nil, :constant => true)
44
- end
45
-
46
- # (rbx-string-dup s) -> (dup s)
47
- def on_rbx_string_dup(node)
48
- node.update(:dup)
49
- end
50
-
51
- # (rbx-pop x) -> x
52
- def on_rbx_pop(node)
53
- child = node.children.first
54
- child.update(:nop) if child.metadata[:constant]
55
-
56
- node.update(:expand, [
57
- child,
58
- AST::Node.new(:jump_target, [], node.metadata)
59
- ], nil)
60
- end
61
-
62
- # (rbx-*) -> .
63
- def make_jump_target(node)
64
- node.update(:jump_target)
65
- end
66
- alias :on_rbx_check_interrupts :make_jump_target
67
- alias :on_rbx_allow_private :make_jump_target
68
-
69
- # (rbx-push-self) -> (self)
70
- def on_rbx_push_self(node)
71
- node.update(:self)
72
- end
73
-
74
- # (rbx-push-local n) -> (get-local n)
75
- def on_rbx_push_local(node)
76
- node.update(:get_lvar, [ @locals[node.children.first] ])
77
- end
78
-
79
- # (rbx-set-local n v) -> (set-local n v)
80
- def on_rbx_set_local(node)
81
- node.update(:set_lvar, [ @locals[node.children.first], node.children.last ])
82
- end
83
-
84
- # (rbx-push-ivar n) -> (get-ivar n)
85
- def on_rbx_push_ivar(node)
86
- node.update(:get_ivar, [ @literals[node.children.first] ])
87
- end
88
-
89
- # (rbx-set-ivar n v) -> (set-ivar n v)
90
- def on_rbx_set_ivar(node)
91
- node.update(:set_ivar, [ @literals[node.children.first], node.children.last ])
92
- end
93
-
94
- # (rbx-push-const-fast n x) -> (const n)
95
- def on_rbx_push_const_fast(node)
96
- node.update(:const, [ node.children.first ])
97
- end
98
-
99
- # (rbx-find-const n c) -> (const n c)
100
- def on_rbx_find_const(node)
101
- node.update(:const, [ @literals[node.children.first], node.children.last ])
102
- end
103
-
104
- # (rbx-ret x) -> (return x)
105
- def on_rbx_ret(node)
106
- node.update(:return)
107
- end
108
-
109
- # (rbx-send-method msg receiver) -> (send msg receiver)
110
- def on_rbx_send_method(node)
111
- node.update(:send, [
112
- AST::MethodName.new(node.children[0]),
113
- node.children[1]
114
- ])
115
- end
116
-
117
- # (rbx-send-stack msg count receiver args...) -> (send msg receiver args...)
118
- def on_rbx_send_stack(node)
119
- node.update(:send, [
120
- AST::MethodName.new(node.children[0]), # message
121
- node.children[2], # receiver
122
- *node.children[3..-1] # args
123
- ])
124
- end
125
-
126
- # (rbx-send-stack-with-block msg count receiver args... block) -> (send-with-block msg receiver args... block)
127
- def on_rbx_send_stack_with_block(node)
128
- node.update(:send_with_block, [
129
- AST::MethodName.new(node.children[0]), # message
130
- node.children[2], # receiver
131
- *node.children[3..-1] # args
132
- ])
133
- end
134
-
135
- # (rbx-create-block block) -> (lambda block)
136
- def on_rbx_create_block(node)
137
- $block = node.children.first
138
- node.update(:lambda, [ "FAIL" ])
139
- end
140
-
141
- # (rbx-meta-* op receiver arg) -> (send op receiver arg)
142
- def on_rbx_send_op_any(node)
143
- node.update(:send, [
144
- AST::MethodName.new(node.children[0]),
145
- *node.children[1..-1]
146
- ])
147
- end
148
- alias :on_rbx_meta_send_op_plus :on_rbx_send_op_any
149
- alias :on_rbx_meta_send_op_minus :on_rbx_send_op_any
150
- alias :on_rbx_meta_send_op_gt :on_rbx_send_op_any
151
- alias :on_rbx_meta_send_op_lt :on_rbx_send_op_any
152
- alias :on_rbx_meta_to_s :on_rbx_send_op_any
153
-
154
- # (rbx-goto-if-* block condition) -> (jump-if block value condition)
155
- def on_rbx_goto_if_false(node)
156
- node.update(:jump_if, [ node.children[0], false, node.children[1] ])
157
- end
158
-
159
- def on_rbx_goto_if_true(node)
160
- node.update(:jump_if, [ node.children[0], true, node.children[1] ])
161
- end
162
-
163
- # (rbx-goto block) -> (jump block)
164
- def on_rbx_goto(node)
165
- node.update(:jump)
166
- end
167
- end
168
- end
169
- end
170
- end
@@ -1,13 +0,0 @@
1
- module Furnace::Transform
2
- end
3
-
4
- require "furnace/transform/pipeline"
5
-
6
- require "furnace/transform/rubinius/ast_build"
7
- require "furnace/transform/rubinius/ast_normalize"
8
-
9
- require "furnace/transform/generic/label_normalize"
10
- require "furnace/transform/generic/cfg_build"
11
- require "furnace/transform/generic/cfg_normalize"
12
-
13
- require "furnace/transform/optimizing/fold_constants"