Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

executable file 212 lines (169 sloc) 5.511 kb
#!/usr/bin/env ruby
require "rubygems"
root_dir = File.expand_path(File.join(File.dirname(__FILE__),'..'))
if File.directory?(File.join(root_dir,'.git'))
Dir.chdir(root_dir) do |path|
require 'bundler'
begin
Bundler.setup(:default)
rescue Bundler::BundlerError => e
warn e.message
warn "Run `bundle install` to install missing gems"
exit e.status_code
end
end
end
lib_dir = File.join(root_dir,'lib')
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
require "trollop"
require "furnace-avm2"
require "thread"
require "benchmark"
require "json"
include Furnace
GRAPH_FORMATS = %w(none text graphviz json)
opts = Trollop::options do
version "furnace-avm2 #{AVM2::VERSION}"
banner <<-EOS
furnace-avm2 is a processing tool which operates on ActionScript3 bytecode.
Supported graphing formats: #{GRAPH_FORMATS.join(", ")}.
Usage: #{$0} [options]
EOS
opt :input, "Input file", :type => :string, :required => true
opt :output, "Output file", :type => :string
opt :only, "Only operate on methods <i+>", :type => :ints, :short => '-O'
opt :except, "Operate on all methods except <i+>", :type => :ints, :short => '-E'
opt :grep, "Search <s> in method names", :type => :string, :short => '-g'
opt :collect, "Collect failed methods instead of exiting", :default => false
opt :sort_by_size, "Sort methods by body size", :default => false
opt :fix_names, "Remove invalid characters from names", :default => true, :short => '-q'
opt :cfg, "Emit CFG in specified format for methods", :type => :string, :short => '-G'
opt :nf, "Build NF-AST", :default => false, :short => '-n'
opt :decompile, "Decompile methods", :default => false, :short => '-d'
opt :wait, "Wait for a tool like profiler to be attached", :type => :boolean, :short => '-w'
end
Trollop::die "Stray arguments: #{ARGV}" unless ARGV.empty?
if opts[:cfg] && !GRAPH_FORMATS.include?(opts[:cfg])
Trollop::die "Unsupported graphing format."
end
abc = nil
File.open(opts[:input]) do |file|
abc = AVM2::ABC::File.new
abc.read(file)
end
failed = []
by_body_size = {}
if opts[:fix_names]
abc.fix_names!
end
if opts[:grep]
regexp = Regexp.new(opts[:grep])
(abc.klasses + abc.instances).each do |scope|
if scope.is_a? AVM2::ABC::InstanceInfo
if scope.name.to_s =~ regexp
puts "Inst Constructor #{scope.name} #{scope.initializer_idx}"
end
type = "Inst "
else
type = "Class"
end
scope.traits.each do |trait|
if [:Function, :Method].include? trait.kind
if trait.name.to_s =~ regexp
puts "#{type} #{trait.kind.to_s.ljust 12} #{trait.name} #{trait.data.method_idx}"
end
end
end
end
exit
end
if opts[:wait]
puts "Press Enter to continue."
gets
end
trap("QUIT") {
puts "Backtrace:"
puts caller
}
threads = []
bodies = abc.method_bodies.dup
loop do
body = bodies.pop
break if body.nil?
if (opts[:except] && opts[:except].include?(body.method_idx)) ||
(opts[:only] && !opts[:only].include?(body.method_idx))
next
end
begin
if opts[:sort_by_size]
by_body_size[body] = body.code_length
end
if opts[:cfg]
cfg, = body.code_to_cfg
if opts[:cfg] == 'graphviz'
File.open("method-#{body.method_idx}.dot", "w") do |dot|
dot.write cfg.to_graphviz
end
elsif opts[:cfg] == 'json'
File.open("method-#{body.method_idx}.json", "w") do |json|
json.write JSON.dump(cfg.to_hash)
end
end
puts "Method #{body.method_idx}; dominators"
cfg.dominators.each do |node, dominating|
puts "#{node.label.inspect} => " +
"#{dominating.map(&:label).map(&:inspect).join(", ")}"
end
puts
=begin
puts "Method #{body.method_idx}; postdominators"
cfg.postdominators.each do |node, dominating|
puts "#{node.label.inspect} => " +
"#{dominating.map(&:label).map(&:inspect).join(", ")}"
end
puts
=end
puts "Method #{body.method_idx}; loops"
cfg.identify_loops.each do |header, body|
puts "#{header.label.inspect} => " +
"#{body.map(&:label).map(&:inspect).join(", ")}"
end
puts
end
if opts[:nf]
ast, = body.code_to_nf
puts "Method #{body.method_idx}; NF-AST"
puts ast.to_sexp
puts
end
if opts[:decompile]
puts "Method #{body.method_idx}"
puts body.decompile(ns: [], debug_funids: true).to_text.lstrip
puts
end
rescue Exception => e
if opts[:collect]
$stderr.puts "Failure at method body idx=#{body.method_idx}: #{e.class} (#{e.message}) at #{e.backtrace.first}."
failed << body.method_idx
else
raise e
end
end
end
if opts[:sort_by_size]
puts "Methods by body size:"
by_body_size.
sort_by { |(body, size)| size }.
each do |(body, size)|
puts "#{size}\tmethod ##{body.method_idx}"
end
end
if opts[:collect] && failed.any?
puts "To skip #{failed.count} failed methods, append this command-line argument:"
puts " --except #{failed.join " "}"
end
if opts[:output]
File.open(opts[:output], "w") do |file|
abc.write(file)
end
end
Jump to Line
Something went wrong with that request. Please try again.