Grammar and stream of consciousness cleanup for Channel and Scheduler rdoc
[rbx.git] / lib / decompiler.rb
blob918c7780a29c5040df2e15288273707e60692bcc
1 require 'kernel/core/iseq' unless defined? RUBY_ENGINE and RUBY_ENGINE == 'rbx'\r
2 \r
3 \r
4 # Like String#unpack('N'), but returns a Fixnum, rather than an array containing a string\r
5 class String\r
6   def unpack_int(endian = ?b)\r
7     if ?b == endian\r
8       # Int is encoded big-endian\r
9       i = (self[3] | (self[2] << 8) | (self[1] << 16) | (self[0] << 24))\r
10     else\r
11       # Int is encoded little-endian\r
12       i = (self[0] | (self[1] << 8) | (self[2] << 16) | (self[3] << 24))\r
13     end\r
14     i\r
15   end\r
16 end\r
20 class MarshalEmitter\r
21   def initialize(ver, str, start=0)\r
22     @rbc_version = ver\r
23     @string = str\r
24     @index = start\r
25     @decoder = InstructionSequence::Encoder.new\r
26   end\r
28   attr_reader :rbc_version\r
30   TagNames = {\r
31     ?n => :nil,\r
32     ?t => :true,\r
33     ?f => :false,\r
34     ?i => :int,\r
35     ?s => :string,\r
36     ?d => :float,\r
37     ?B => :bignum,\r
38     ?x => :symbol,\r
39     ?b => :bytes,\r
40     ?I => :instructions,\r
41     ?p => :tuple,\r
42     ?A => :array,\r
43     ?m => :method,\r
44     ?M => :method2,\r
45     ?r => :object,\r
46     ?S => :send_site\r
47   }\r
49   NoBody = [:nil, :true, :false]\r
51   def self.process_rbc(file)\r
52     # Binary mode needs to be specified on Win\r
53     str = ""\r
54     File.open(file, 'rb') do |f|\r
55       str = f.read\r
56     end\r
57     raise "Not a Rubinius compiled file" unless 'RBIX' == str[0..3]\r
58     ver = str[4..7].unpack_int\r
60     return new(ver, str, 28)\r
61   end\r
63   def process\r
64     tag = @string[@index]\r
65     @index += 1\r
67     name = TagNames[tag]\r
68     raise "Unrecognised tag '" << (tag || '\0') << "' at #{@index} (#{sprintf('%#x', @index)})" unless name\r
70     if NoBody.include? name\r
71       name\r
72     else\r
73       body = __send__ "process_#{name}"\r
74       if name == :bignum\r
75         body.to_i\r
76       else\r
77         [name, body]\r
78       end\r
79     end\r
80   end\r
82   def process_int\r
83     body = @string[@index, 5]\r
84     @index += 5\r
85     sign = body[0]\r
86     int = body[1..-1].unpack_int\r
87     if sign == ?n\r
88       int = -int\r
89     end\r
90     return int\r
91   end\r
93   def process_string\r
94     sz = @string[@index,4].unpack_int\r
95     @index += 4\r
96     body = @string[@index, sz]\r
97     @index += sz\r
98     return body\r
99   end\r
101   def process_num\r
102     body = process_string\r
103     @index += 1  # Discard trailing \0\r
104     body\r
105   end\r
107   alias :process_float     :process_num\r
108   alias :process_bignum    :process_num\r
109   alias :process_symbol    :process_string\r
110   alias :process_bytes     :process_string\r
111   alias :process_send_site :process_string\r
113   def process_tuple\r
114     sz = @string[@index,4].unpack_int\r
115     @index += 4\r
116     body = []\r
117     sz.times do\r
118       body << process()\r
119     end\r
121     body\r
122   end\r
124   alias :process_method :process_tuple\r
125   alias :process_object :process_tuple\r
127   # Support for version 2 of compiled method, which replaces size with a version number\r
128   def process_method2\r
129     ver = @string[@index,4].unpack_int\r
130     @index += 4\r
131     body = []\r
132     sz = 16 if 1 == ver\r
133     raise "Unsupported version (#{ver}) of CompiledMethod" unless sz\r
135     sz.times do\r
136       body << process()\r
137     end\r
139     body\r
140   end\r
142   def process_instructions\r
143     endian = @string[@index]\r
144     @index += 1\r
145     body = process_string()\r
146     body = @decoder.decode_iseq(body)\r
147     body.map! {|i| [i.first.opcode].concat i[1..-1]}\r
148     body\r
149   end\r
150 end\r
152 # If file is run, dump content of .rbc to STDOUT\r
153 if __FILE__ == $0\r
154   if ARGV.size > 0\r
155     require 'pp'\r
156     while rbc = ARGV.shift\r
157       emit = MarshalEmitter.process_rbc(rbc)\r
158       STDOUT.puts "\nContent of #{rbc}:"\r
159       pp emit.process\r
160     end\r
161   else\r
162     STDOUT.puts "Usage: #{__FILE__} <rbc_file> [<rbc_file> ...]"\r
163   end\r