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.
- data/.gitignore +1 -0
- data/Gemfile +3 -1
- data/bin/furnace-avm2 +27 -13
- data/bin/furnace-avm2-decompiler +14 -4
- data/bin/furnace-avm2-shell +4 -0
- data/furnace-avm2.gemspec +1 -1
- data/lib/furnace-avm2.rb +1 -1
- data/lib/furnace-avm2/abc.rb +3 -0
- data/lib/furnace-avm2/abc/metadata/exception_info.rb +7 -2
- data/lib/furnace-avm2/abc/metadata/method_body_info.rb +4 -2
- data/lib/furnace-avm2/abc/metadata/script_info.rb +8 -3
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_add_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_declocal_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_decrement_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_divide.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_equals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_greaterthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_inclocal_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_increment_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_lessthan.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_modulo.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_multiply_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_negate_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_not.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_strictequals.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic/as3_subtract_i.rb +2 -1
- data/lib/furnace-avm2/abc/opcodes/arithmetic_opcode.rb +5 -0
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvalue.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return/as3_returnvoid.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/function_return_opcode.rb +4 -0
- data/lib/furnace-avm2/abc/opcodes/opcode.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce.rb +1 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_a.rb +4 -1
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_b.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_coerce_s.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_d.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_i.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_o.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_s.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion/as3_convert_u.rb +3 -2
- data/lib/furnace-avm2/abc/opcodes/type_conversion_opcode.rb +10 -0
- data/lib/furnace-avm2/abc/primitives/opcode_sequence.rb +4 -0
- data/lib/furnace-avm2/abc/primitives/record.rb +21 -1
- data/lib/furnace-avm2/source/declaration_tokens/package_token.rb +16 -8
- data/lib/furnace-avm2/source/declaration_tokens/script_token.rb +7 -0
- data/lib/furnace-avm2/source/declaration_tokens/slot_token.rb +12 -4
- data/lib/furnace-avm2/source/declaration_tokens/token_with_traits.rb +10 -9
- data/lib/furnace-avm2/source/decompiler.rb +363 -166
- data/lib/furnace-avm2/source/implementation_tokens/case_token.rb +20 -0
- data/lib/furnace-avm2/source/implementation_tokens/catch_filter_token.rb +7 -0
- data/lib/furnace-avm2/source/implementation_tokens/catch_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/closure_token.rb +2 -0
- data/lib/furnace-avm2/source/implementation_tokens/control_flow_token.rb +0 -1
- data/lib/furnace-avm2/source/implementation_tokens/do_while_token.rb +16 -0
- data/lib/furnace-avm2/source/implementation_tokens/else_token.rb +3 -1
- data/lib/furnace-avm2/source/implementation_tokens/finally_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/label_name_token.rb +12 -0
- data/lib/furnace-avm2/source/implementation_tokens/this_token.rb +9 -0
- data/lib/furnace-avm2/source/implementation_tokens/try_token.rb +11 -0
- data/lib/furnace-avm2/source/implementation_tokens/unary_operator_token.rb +2 -2
- data/lib/furnace-avm2/source/implementation_tokens/unary_post_operator_token.rb +2 -2
- data/lib/furnace-avm2/transform.rb +2 -0
- data/lib/furnace-avm2/transform/ast_build.rb +234 -92
- data/lib/furnace-avm2/transform/ast_normalize.rb +4 -13
- data/lib/furnace-avm2/transform/cfg_build.rb +74 -33
- data/lib/furnace-avm2/transform/cfg_reduce.rb +457 -75
- data/lib/furnace-avm2/transform/nf_normalize.rb +69 -40
- data/lib/furnace-avm2/transform/propagate_constants.rb +49 -0
- data/lib/furnace-avm2/transform/propagate_labels.rb +31 -0
- data/lib/furnace-avm2/version.rb +1 -1
- data/test/basic.as +111 -3
- data/test/switch.as +27 -0
- metadata +907 -313
- data/Gemfile.lock +0 -19
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/bin/furnace-avm2
CHANGED
|
@@ -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 :
|
|
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.
|
|
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
|
-
|
|
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[:
|
|
131
|
-
|
|
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[:
|
|
204
|
-
puts "
|
|
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?
|
data/bin/furnace-avm2-decompiler
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
3
|
require "rubygems"
|
|
4
|
-
|
|
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
|
|
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
|
-
|
|
152
|
-
|
|
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
|
|
data/bin/furnace-avm2-shell
CHANGED
data/furnace-avm2.gemspec
CHANGED
|
@@ -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", '
|
|
20
|
+
s.add_runtime_dependency "furnace", '= 0.2.3'
|
|
21
21
|
s.add_runtime_dependency "trollop"
|
|
22
22
|
end
|
data/lib/furnace-avm2.rb
CHANGED
data/lib/furnace-avm2/abc.rb
CHANGED
|
@@ -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
|
-
|
|
7
|
-
const_ref :
|
|
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={
|
|
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
|
|
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:
|
|
24
|
+
package_name: package_name)
|
|
24
25
|
)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
|
-
def
|
|
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 AS3IncLocalI <
|
|
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 ]
|