furnace-avm2 0.9.2 → 1.0.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.
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