furnace 0.1.2 → 0.2.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.
@@ -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"