furnace-avm2 0.9.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +3 -1
  3. data/bin/furnace-avm2 +27 -13
  4. data/bin/furnace-avm2-decompiler +14 -4
  5. data/bin/furnace-avm2-shell +4 -0
  6. data/furnace-avm2.gemspec +1 -1
  7. data/lib/furnace-avm2.rb +1 -1
  8. data/lib/furnace-avm2/abc.rb +3 -0
  9. data/lib/furnace-avm2/abc/metadata/exception_info.rb +7 -2
  10. data/lib/furnace-avm2/abc/metadata/method_body_info.rb +4 -2
  11. data/lib/furnace-avm2/abc/metadata/script_info.rb +8 -3
  12. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add.rb +1 -1
  13. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add_i.rb +2 -1
  14. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal.rb +1 -1
  15. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal_i.rb +2 -1
  16. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement.rb +1 -1
  17. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement_i.rb +2 -1
  18. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_divide.rb +1 -1
  19. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_equals.rb +1 -1
  20. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterequals.rb +1 -1
  21. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterthan.rb +1 -1
  22. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal.rb +1 -1
  23. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal_i.rb +2 -1
  24. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment.rb +1 -1
  25. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment_i.rb +2 -1
  26. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessequals.rb +1 -1
  27. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessthan.rb +1 -1
  28. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_modulo.rb +1 -1
  29. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply.rb +1 -1
  30. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply_i.rb +2 -1
  31. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate.rb +1 -1
  32. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate_i.rb +2 -1
  33. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_not.rb +1 -1
  34. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_strictequals.rb +1 -1
  35. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract.rb +1 -1
  36. data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract_i.rb +2 -1
  37. data/lib/furnace-avm2/abc/opcodes/arithmetic_opcode.rb +5 -0
  38. data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvalue.rb +1 -1
  39. data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvoid.rb +1 -1
  40. data/lib/furnace-avm2/abc/opcodes/function_return_opcode.rb +4 -0
  41. data/lib/furnace-avm2/abc/opcodes/opcode.rb +1 -1
  42. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce.rb +1 -1
  43. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_a.rb +4 -1
  44. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_b.rb +3 -2
  45. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_s.rb +3 -2
  46. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_d.rb +3 -2
  47. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_i.rb +3 -2
  48. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_o.rb +3 -2
  49. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_s.rb +3 -2
  50. data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_u.rb +3 -2
  51. data/lib/furnace-avm2/abc/opcodes/type_conversion_opcode.rb +10 -0
  52. data/lib/furnace-avm2/abc/primitives/opcode_sequence.rb +4 -0
  53. data/lib/furnace-avm2/abc/primitives/record.rb +21 -1
  54. data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +16 -8
  55. data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +7 -0
  56. data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +12 -4
  57. data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +10 -9
  58. data/lib/furnace-avm2/source/decompiler.rb +363 -166
  59. data/lib/furnace-avm2/source/implementation_tokens/case_token.rb +20 -0
  60. data/lib/furnace-avm2/source/implementation_tokens/catch_filter_token.rb +7 -0
  61. data/lib/furnace-avm2/source/implementation_tokens/catch_token.rb +9 -0
  62. data/lib/furnace-avm2/source/implementation_tokens/closure_token.rb +2 -0
  63. data/lib/furnace-avm2/source/implementation_tokens/control_flow_token.rb +0 -1
  64. data/lib/furnace-avm2/source/implementation_tokens/do_while_token.rb +16 -0
  65. data/lib/furnace-avm2/source/implementation_tokens/else_token.rb +3 -1
  66. data/lib/furnace-avm2/source/implementation_tokens/finally_token.rb +11 -0
  67. data/lib/furnace-avm2/source/implementation_tokens/label_name_token.rb +12 -0
  68. data/lib/furnace-avm2/source/implementation_tokens/this_token.rb +9 -0
  69. data/lib/furnace-avm2/source/implementation_tokens/try_token.rb +11 -0
  70. data/lib/furnace-avm2/source/implementation_tokens/unary_operator_token.rb +2 -2
  71. data/lib/furnace-avm2/source/implementation_tokens/unary_post_operator_token.rb +2 -2
  72. data/lib/furnace-avm2/transform.rb +2 -0
  73. data/lib/furnace-avm2/transform/ast_build.rb +234 -92
  74. data/lib/furnace-avm2/transform/ast_normalize.rb +4 -13
  75. data/lib/furnace-avm2/transform/cfg_build.rb +74 -33
  76. data/lib/furnace-avm2/transform/cfg_reduce.rb +457 -75
  77. data/lib/furnace-avm2/transform/nf_normalize.rb +69 -40
  78. data/lib/furnace-avm2/transform/propagate_constants.rb +49 -0
  79. data/lib/furnace-avm2/transform/propagate_labels.rb +31 -0
  80. data/lib/furnace-avm2/version.rb +1 -1
  81. data/test/basic.as +111 -3
  82. data/test/switch.as +27 -0
  83. metadata +907 -313
  84. data/Gemfile.lock +0 -19
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  .rbx/
2
2
  pkg/
3
3
  *.abc
4
+ Gemfile.lock
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gemspec
3
+ gemspec
4
+
5
+ gem "pry"
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubygems"
4
+ begin
5
+ require "bundler/setup"
6
+ rescue LoadError
7
+ end
4
8
 
5
9
  $: << File.join(File.dirname(__FILE__), '..', 'lib')
6
10
 
@@ -30,7 +34,7 @@ EOS
30
34
  opt :grep, "Search <s> in method names", :type => :string, :short => '-G'
31
35
 
32
36
  opt :collect, "Collect failed methods instead of exiting", :default => false
33
- opt :smallest, "Find method with smallest body", :default => false
37
+ opt :sort_by_size, "Sort methods by body size", :default => false
34
38
 
35
39
  opt :disasm_before, "Disassemble methods before transforming", :default => false, :short => '-B'
36
40
  opt :disasm_after, "Disassemble methods after transforming", :default => false, :short => '-A'
@@ -66,7 +70,7 @@ disasm = lambda do |body, after|
66
70
  puts "Exceptions"
67
71
  body.exceptions.each do |exception|
68
72
  puts " #{exception.from_offset} -> #{exception.to_offset}: " <<
69
- "catch(#{exception.exc_type} #{exception.var_name}) #{exception.target_offset}"
73
+ "catch(#{exception.exception.to_s} #{exception.variable.to_s}) #{exception.target_offset}"
70
74
  end
71
75
  end
72
76
 
@@ -75,7 +79,7 @@ end
75
79
 
76
80
  failed = []
77
81
  dced = []
78
- smallest = nil
82
+ by_body_size = {}
79
83
 
80
84
  if opts[:fix_names]
81
85
  abc.fix_names!
@@ -127,10 +131,8 @@ opts[:threads].times do
127
131
  begin
128
132
  disasm[body, false] if opts[:disasm_before]
129
133
 
130
- if opts[:smallest]
131
- if smallest.nil? || smallest.code_length > body.code_length
132
- smallest = body
133
- end
134
+ if opts[:sort_by_size]
135
+ by_body_size[body] = body.code_length
134
136
  end
135
137
 
136
138
  if opts[:dce]
@@ -140,14 +142,14 @@ opts[:threads].times do
140
142
  disasm[body, true] if opts[:disasm_after]
141
143
 
142
144
  if opts[:ast]
143
- ast = body.code_to_ast
145
+ ast, = body.code_to_ast
144
146
  puts "Method #{body.method_idx}; AST"
145
147
  puts ast.to_sexp
146
148
  puts
147
149
  end
148
150
 
149
151
  if opts[:cfg]
150
- cfg = body.code_to_cfg
152
+ cfg, = body.code_to_cfg
151
153
  File.open("method-#{body.method_idx}.dot", "w") do |dot|
152
154
  dot.write cfg.to_graphviz
153
155
  end
@@ -158,7 +160,14 @@ opts[:threads].times do
158
160
  "#{dominating.map(&:label).map(&:inspect).join(", ")}"
159
161
  end
160
162
  puts
161
-
163
+ =begin
164
+ puts "Method #{body.method_idx}; postdominators"
165
+ cfg.postdominators.each do |node, dominating|
166
+ puts "#{node.label.inspect} => " +
167
+ "#{dominating.map(&:label).map(&:inspect).join(", ")}"
168
+ end
169
+ puts
170
+ =end
162
171
  puts "Method #{body.method_idx}; loops"
163
172
  cfg.identify_loops.each do |header, body|
164
173
  puts "#{header.label.inspect} => " +
@@ -168,7 +177,7 @@ opts[:threads].times do
168
177
  end
169
178
 
170
179
  if opts[:nf]
171
- ast = body.code_to_nf
180
+ ast, = body.code_to_nf
172
181
  puts "Method #{body.method_idx}; NF-AST"
173
182
  puts ast.to_sexp
174
183
  puts
@@ -200,8 +209,13 @@ if opts[:verbose]
200
209
  end
201
210
  end
202
211
 
203
- if opts[:smallest]
204
- puts "Smallest method is #{smallest.method_idx} with #{smallest.code_length} bytes"
212
+ if opts[:sort_by_size]
213
+ puts "Methods by body size:"
214
+ by_body_size.
215
+ sort_by { |(body, size)| size }.
216
+ each do |(body, size)|
217
+ puts "#{size}\tmethod ##{body.method_idx}"
218
+ end
205
219
  end
206
220
 
207
221
  if opts[:collect] && failed.any?
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubygems"
4
- require "bundler/setup"
4
+ begin
5
+ require "bundler/setup"
6
+ rescue LoadError
7
+ end
5
8
 
6
9
  $: << File.join(File.dirname(__FILE__), '..', 'lib')
7
10
 
@@ -102,7 +105,7 @@ start_time = Time.now
102
105
  mutex = Mutex.new
103
106
 
104
107
  roots = {}
105
- workqueue = abc.instances.dup + abc.scripts.select(&:any_code?)
108
+ workqueue = abc.instances + abc.scripts
106
109
  total_size = workqueue.size
107
110
 
108
111
  last_percentage = 0
@@ -148,8 +151,14 @@ opts[:threads].times.map do
148
151
  name = what.name
149
152
  ns = name.ns
150
153
  else # ScriptInfo
151
- name = what.traits[0].name.ns
152
- ns = name.to_s.sub(/(^|\.)[^.]+$/, '')
154
+ if what.has_name?
155
+ name = what.package_name.ns
156
+ ns = name.to_s.sub(/(^|\.)[^.]+$/, '')
157
+ else
158
+ index = abc.scripts.index(what)
159
+ name = "__global_name_#{index}"
160
+ ns = "__global_ns_#{index}"
161
+ end
153
162
  end
154
163
  next if shound_skip.(name)
155
164
 
@@ -164,6 +173,7 @@ opts[:threads].times.map do
164
173
 
165
174
  source = what.decompile(options)
166
175
  next unless source
176
+ next unless source.children.any?
167
177
 
168
178
  text = source.to_text
169
179
 
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "rubygems"
4
+ begin
5
+ require "bundler/setup"
6
+ rescue LoadError
7
+ end
4
8
 
5
9
  $: << File.join(File.dirname(__FILE__), '..', 'lib')
6
10
 
@@ -17,6 +17,6 @@ Gem::Specification.new do |s|
17
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
18
  s.require_paths = ["lib"]
19
19
 
20
- s.add_runtime_dependency "furnace", '>= 0.1.1'
20
+ s.add_runtime_dependency "furnace", '= 0.2.3'
21
21
  s.add_runtime_dependency "trollop"
22
22
  end
@@ -1,7 +1,7 @@
1
1
  require "furnace/cfg"
2
2
  require "furnace/ast"
3
3
  require "furnace/code"
4
- require "furnace/transform"
4
+ require "furnace/transform/pipeline"
5
5
 
6
6
  require "furnace/graphviz"
7
7
 
@@ -16,7 +16,10 @@ require_relative "abc/opcodes/load_store_opcode"
16
16
  require_relative "abc/opcodes/push_literal_opcode"
17
17
  require_relative "abc/opcodes/control_transfer_opcode"
18
18
  require_relative "abc/opcodes/function_invocation_opcode"
19
+ require_relative "abc/opcodes/function_return_opcode"
19
20
  require_relative "abc/opcodes/property_opcode"
21
+ require_relative "abc/opcodes/type_conversion_opcode"
22
+ require_relative "abc/opcodes/arithmetic_opcode"
20
23
 
21
24
  Dir[File.join(File.dirname(__FILE__), "abc", "opcodes", "*", "*.rb")].each do |file|
22
25
  require file
@@ -3,8 +3,9 @@ module Furnace::AVM2::ABC
3
3
  vuint30 :from_offset
4
4
  vuint30 :to_offset
5
5
  vuint30 :target_offset
6
- const_ref :exc_type, :string
7
- const_ref :var_name, :string
6
+
7
+ const_ref :exception, :multiname
8
+ const_ref :variable, :multiname
8
9
 
9
10
  attr_reader :from, :to, :target
10
11
 
@@ -25,5 +26,9 @@ module Furnace::AVM2::ABC
25
26
  self.to_offset = @to.offset
26
27
  self.target_offset = @target.offset
27
28
  end
29
+
30
+ def range
31
+ from_offset..to_offset
32
+ end
28
33
  end
29
34
  end
@@ -21,10 +21,11 @@ module Furnace::AVM2::ABC
21
21
  end
22
22
  end
23
23
 
24
- def code_to_ast(options={ eliminate_nops: true })
24
+ def code_to_ast(options={})
25
25
  pipeline = Furnace::Transform::Pipeline.new([
26
26
  Furnace::AVM2::Transform::ASTBuild.new(options),
27
- Furnace::AVM2::Transform::ASTNormalize.new(options)
27
+ Furnace::AVM2::Transform::ASTNormalize.new(options),
28
+ Furnace::AVM2::Transform::PropagateConstants.new,
28
29
  ])
29
30
 
30
31
  pipeline.run(code, self)
@@ -32,6 +33,7 @@ module Furnace::AVM2::ABC
32
33
 
33
34
  def code_to_cfg
34
35
  pipeline = Furnace::Transform::Pipeline.new([
36
+ Furnace::AVM2::Transform::PropagateLabels.new,
35
37
  Furnace::AVM2::Transform::CFGBuild.new
36
38
  ])
37
39
 
@@ -8,7 +8,8 @@ module Furnace::AVM2::ABC
8
8
  abc_array_of :trait, :nested, :class => TraitInfo
9
9
 
10
10
  def collect_ns
11
- options = { ns: Set.new([ non_init_traits[0].name.ns ]), no_ns: Set.new, names: {} }
11
+ options = { ns: Set.new, no_ns: Set.new, names: {} }
12
+ options[:ns].add package_name.ns if has_name?
12
13
 
13
14
  initializer.collect_ns(options) if initializer
14
15
  traits.each { |trait| trait.collect_ns(options) }
@@ -20,14 +21,18 @@ module Furnace::AVM2::ABC
20
21
  Furnace::AVM2::Tokens::PackageToken.new(self,
21
22
  options.merge(collect_ns).merge(
22
23
  package_type: :script,
23
- package_name: non_init_traits[0].name)
24
+ package_name: package_name)
24
25
  )
25
26
  end
26
27
 
27
- def any_code?
28
+ def has_name?
28
29
  non_init_traits.any?
29
30
  end
30
31
 
32
+ def package_name
33
+ non_init_traits[0].name if non_init_traits.any?
34
+ end
35
+
31
36
  def non_init_traits
32
37
  traits.reject { |trait| trait.kind == :Class }
33
38
  end
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3Add < Opcode
2
+ class AS3Add < ArithmeticOpcode
3
3
  instruction 0xa0
4
4
 
5
5
  consume 2
@@ -1,10 +1,11 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3AddI < Opcode
2
+ class AS3AddI < ArithmeticOpcode
3
3
  instruction 0xc5
4
4
 
5
5
  consume 2
6
6
  produce 1
7
7
 
8
8
  type :integer
9
+ ast_type :add
9
10
  end
10
11
  end
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3DecLocal < Opcode
2
+ class AS3DecLocal < ArithmeticOpcode
3
3
  instruction 0x94
4
4
 
5
5
  body do
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3DecLocalI < Opcode
2
+ class AS3DecLocalI < ArithmeticOpcode
3
3
  instruction 0xc3
4
4
 
5
5
  body do
@@ -10,5 +10,6 @@ module Furnace::AVM2::ABC
10
10
  produce 0
11
11
 
12
12
  type :integer
13
+ ast_type :dec_local
13
14
  end
14
15
  end
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3Decrement < Opcode
2
+ class AS3Decrement < ArithmeticOpcode
3
3
  instruction 0x93
4
4
 
5
5
  consume 1
@@ -1,10 +1,11 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3DecrementI < Opcode
2
+ class AS3DecrementI < ArithmeticOpcode
3
3
  instruction 0xc1
4
4
 
5
5
  consume 1
6
6
  produce 1
7
7
 
8
8
  type :integer
9
+ ast_type :decrement
9
10
  end
10
11
  end
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3Divide < Opcode
2
+ class AS3Divide < ArithmeticOpcode
3
3
  instruction 0xa3
4
4
 
5
5
  consume 2
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3Equals < Opcode
2
+ class AS3Equals < ArithmeticOpcode
3
3
  instruction 0xab
4
4
  ast_type :==
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3GreaterEquals < Opcode
2
+ class AS3GreaterEquals < ArithmeticOpcode
3
3
  instruction 0xb0
4
4
  ast_type :>=
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3GreaterThan < Opcode
2
+ class AS3GreaterThan < ArithmeticOpcode
3
3
  instruction 0xaf
4
4
  ast_type :>
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3IncLocal < Opcode
2
+ class AS3IncLocal < ArithmeticOpcode
3
3
  instruction 0x92
4
4
 
5
5
  body do
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3IncLocalI < Opcode
2
+ class AS3IncLocalI < ArithmeticOpcode
3
3
  instruction 0xc2
4
4
 
5
5
  body do
@@ -10,6 +10,7 @@ module Furnace::AVM2::ABC
10
10
  produce 0
11
11
 
12
12
  type :integer
13
+ ast_type :inc_local
13
14
 
14
15
  def parameters
15
16
  [ body.reg_index ]
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3Increment < Opcode
2
+ class AS3Increment < ArithmeticOpcode
3
3
  instruction 0x91
4
4
 
5
5
  consume 1
@@ -1,10 +1,11 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3IncrementI < Opcode
2
+ class AS3IncrementI < ArithmeticOpcode
3
3
  instruction 0xc0
4
4
 
5
5
  consume 1
6
6
  produce 1
7
7
 
8
8
  type :integer
9
+ ast_type :increment
9
10
  end
10
11
  end
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3LessEquals < Opcode
2
+ class AS3LessEquals < ArithmeticOpcode
3
3
  instruction 0xae
4
4
  ast_type :<=
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3LessThan < Opcode
2
+ class AS3LessThan < ArithmeticOpcode
3
3
  instruction 0xad
4
4
  ast_type :<
5
5
 
@@ -1,5 +1,5 @@
1
1
  module Furnace::AVM2::ABC
2
- class AS3Modulo < Opcode
2
+ class AS3Modulo < ArithmeticOpcode
3
3
  instruction 0xa4
4
4
 
5
5
  consume 2