ruby-decompiler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ require 'internal/node/as_code'
2
+ require 'internal/proc'
3
+ require 'internal/proc/signature'
4
+
5
+ class Proc
6
+ # Return a string representation of a proc's definition/body,
7
+ # similarly to +Method#as_code+.
8
+ def as_code(indent=0)
9
+ sig = self.signature
10
+ body_expression = self.body ? self.body.as_code(indent+1) : nil
11
+ s = "#{' '*indent}proc do"
12
+ if not sig.args.unspecified then
13
+ s += " #{sig}"
14
+ end
15
+ s += "\n"
16
+ if body_expression then
17
+ s += "#{body_expression}\n"
18
+ end
19
+ s += "#{' '*indent}end"
20
+ return s
21
+ end
22
+ end
23
+
@@ -0,0 +1,16 @@
1
+ require 'internal/node/as_expression'
2
+ require 'internal/proc/signature'
3
+ require 'internal/proc'
4
+
5
+ class Proc
6
+ # Return a single-line string representation of a proc's
7
+ # definition/body, similarly to +Method#as_expression+.
8
+ def as_expression
9
+ sig = self.signature
10
+ body_expression = self.body ? self.body.as_expression : nil
11
+ s = sig.args.unspecified ? "" : sig.to_s + ' '
12
+ b = body_expression ? body_expression + ' ' : ''
13
+ return "proc { #{s}#{b}}"
14
+ end
15
+ end
16
+
@@ -0,0 +1,184 @@
1
+ require 'internal/node'
2
+ require 'internal/proc'
3
+ require 'internal/vm'
4
+
5
+ class Proc
6
+ class Arguments
7
+ include Enumerable
8
+
9
+ def initialize(names, multiple_assignment, rest_arg)
10
+ @names = names
11
+ @multiple_assignment = multiple_assignment
12
+ @rest_arg = rest_arg
13
+ end
14
+
15
+ def unspecified
16
+ if defined?(RubyVM) and defined?(RubyVM::InstructionSequence) then
17
+ # YARV
18
+ return @names.length == 0
19
+ else
20
+ # pre-YARV
21
+ @names.nil?
22
+ end
23
+ end
24
+
25
+ def single_assignment
26
+ !@multiple_assignment
27
+ end
28
+
29
+ def multiple_assignment
30
+ @multiple_assignment
31
+ end
32
+
33
+ def names
34
+ @names
35
+ end
36
+
37
+ def [](idx)
38
+ @names[idx]
39
+ end
40
+
41
+ def each(&block)
42
+ (@names || []).each(&block)
43
+ end
44
+
45
+ def size
46
+ @names.size
47
+ end
48
+
49
+ def rest_arg
50
+ @rest_arg
51
+ end
52
+
53
+ def empty_last_arg
54
+ if defined?(VM) and defined?(VM::InstructionSequence) then
55
+ # YARV
56
+ return (@rest_arg and @names[-1] == nil)
57
+ else
58
+ # pre-YARV
59
+ return (@names.size == 1 and not @rest_arg)
60
+ end
61
+ end
62
+ end
63
+
64
+ # Return an Arguments object representing the arguments in the order
65
+ # in which they appear in the argument list.
66
+ def arguments
67
+ has_rest_arg = self.has_rest_arg
68
+
69
+ if self.respond_to?(:var) then
70
+ # pre-YARV
71
+ case self.var
72
+ when Node::DASGN_CURR
73
+ return Arguments.new([ self.var.vid ], false, has_rest_arg ? 0 : nil)
74
+ when Node::MASGN
75
+ if self.var.head then
76
+ a = self.var.head.to_a
77
+ args = a.map { |n| n.vid }
78
+ else
79
+ args = []
80
+ end
81
+ if self.var.args then
82
+ args.push(self.var.args.vid)
83
+ end
84
+ return Arguments.new(args, true, has_rest_arg ? args.size - 1: nil)
85
+ when nil
86
+ return Arguments.new(nil, false, has_rest_arg ? 0 : nil)
87
+ when Fixnum
88
+ return Arguments.new([], false, has_rest_arg ? 0 : nil)
89
+ else
90
+ raise "Unexpected node type: #{self.var.class}"
91
+ end
92
+ elsif
93
+ # YARV
94
+ iseq = self.body
95
+ local_vars = iseq.local_table
96
+ has_rest_arg = iseq.arg_rest != -1
97
+ has_block_arg = iseq.arg_block != -1
98
+ num_args = \
99
+ iseq.argc + \
100
+ iseq.arg_opt_table.size + \
101
+ (has_rest_arg ? 1 : 0) + \
102
+ (has_block_arg ? 1 : 0)
103
+ names = local_vars[0...num_args]
104
+ # TODO: masgn
105
+ return Arguments.new(names, true, has_rest_arg ? -1 : nil)
106
+ else
107
+ return Arguments.new(nil, false, nil)
108
+ end
109
+ end
110
+
111
+ # Return true if the proc has a rest arg
112
+ def has_rest_arg
113
+ if self.respond_to?(:var) then
114
+ # pre-YARV
115
+ has_rest_arg = false
116
+ if self.var then
117
+ if self.var.class == Node::MASGN then
118
+ if self.var.args then
119
+ has_rest_arg = true
120
+ end
121
+ end
122
+ end
123
+ else
124
+ # YARV
125
+ rest = self.body.arg_rest
126
+ has_rest_arg = (rest >= 0 ? rest - 1 : nil)
127
+ end
128
+ return has_rest_arg
129
+ end
130
+
131
+ # Return a hash mapping each argument name to a description of that
132
+ # argument.
133
+ def argument_info
134
+ args = self.arguments()
135
+
136
+ info = {}
137
+ args.each do |name|
138
+ info[name] = name.to_s
139
+ end
140
+
141
+ # Rest arg
142
+ if args.rest_arg then
143
+ rest_name = args[args.rest_arg]
144
+ if rest_name then
145
+ info[rest_name] = "*#{rest_name}"
146
+ end
147
+ end
148
+
149
+ return info
150
+ end
151
+
152
+ class Signature
153
+ attr_reader :args, :arg_info
154
+
155
+ def initialize(args, arg_info)
156
+ @args = args
157
+ @arg_info = arg_info
158
+ end
159
+
160
+ def to_s
161
+ if @args.unspecified then
162
+ return ""
163
+ elsif @args.multiple_assignment then
164
+ if @args.empty_last_arg then
165
+ params = @args.map{ |n| arg_info[n] }
166
+ return "|#{@arg_info[@args[0]]},|"
167
+ else
168
+ params = @args.map{ |n| arg_info[n] }
169
+ return "|#{params.join(', ')}|"
170
+ end
171
+ else
172
+ return "|#{@arg_info[@args[0]]}|"
173
+ end
174
+ end
175
+ end
176
+
177
+ # Return a String representing the method's signature.
178
+ def signature
179
+ return Signature.new(
180
+ arguments(),
181
+ argument_info)
182
+ end
183
+ end
184
+
@@ -0,0 +1,866 @@
1
+ require 'internal/vm/iseq'
2
+ require 'internal/vm/instruction'
3
+ require 'internal/vm/inline_cache'
4
+ require 'internal/vm/constants'
5
+
6
+ module Internal
7
+
8
+ # A module for decoding YARV bytecode.
9
+ #
10
+ # This is actually pretty cool. It's actually a miniature VM, where the
11
+ # result of evaluating an expression is itself another expression. This
12
+ # turns out to be much simpler than a full ruby VM, but I think one
13
+ # could use this as a base for building one.
14
+ #
15
+ # Example usage:
16
+ # env = Internal::ByteDecoder::Environment.new(is.local_table)
17
+ # is = RubyVM::InstructionSequence.new('1 + 1')
18
+ # is.bytedecode(env)
19
+ # env.expressions.each do |expr|
20
+ # puts expr
21
+ # end
22
+ # puts stack[-1]
23
+ #
24
+ module ByteDecoder
25
+
26
+ class Environment
27
+ attr_reader :stack
28
+ attr_reader :expressions
29
+ attr_reader :local_table
30
+ attr_accessor :last
31
+ attr_reader :seq
32
+ attr_accessor :pc
33
+
34
+ def initialize(local_table)
35
+ @stack = []
36
+ @expressions = []
37
+ @local_table = local_table
38
+ @last = nil
39
+ @pc = 0
40
+ end
41
+
42
+ def advance(instruction_length)
43
+ @pc += instruction_length
44
+ end
45
+
46
+ def remember(expression)
47
+ if not expression.is_a?(Expression::Literal) then
48
+ @expressions << expression
49
+ end
50
+ end
51
+ end
52
+
53
+ class Expression
54
+ attr_reader :pc
55
+
56
+ def initialize(pc)
57
+ @pc = pc
58
+ end
59
+
60
+ def <=>(rhs)
61
+ return @pc <=> rhs.pc
62
+ end
63
+
64
+ def fmt(arg)
65
+ if arg.respond_to?(:precedence)
66
+ p = arg.precedence
67
+ else
68
+ p = 0
69
+ end
70
+
71
+ if p >= self.precedence then
72
+ return "(#{arg})"
73
+ else
74
+ return arg
75
+ end
76
+ end
77
+
78
+ class Literal < Expression
79
+ attr_reader :value
80
+
81
+ def initialize(pc, value)
82
+ super(pc)
83
+ @value = value
84
+ end
85
+
86
+ def to_s
87
+ case @value
88
+ when Regexp then "/#{@value.inspect[1..-2]}/"
89
+ else; return @value.inspect
90
+ end
91
+ end
92
+
93
+ def precedence
94
+ return 1
95
+ end
96
+ end
97
+
98
+ class Infix < Expression
99
+ attr_reader :op
100
+ attr_reader :lhs
101
+ attr_reader :rhs
102
+
103
+ def initialize(pc, op, lhs, rhs)
104
+ super(pc)
105
+ @op = op
106
+ @lhs = lhs
107
+ @rhs = rhs
108
+ end
109
+
110
+ def to_s
111
+ return "#{fmt(@lhs)} #{@op} #{fmt(@rhs)}"
112
+ end
113
+
114
+ def precedence
115
+ case @op
116
+ when :*, :/, :%
117
+ return 2
118
+ when '+'.intern, '-'.intern
119
+ return 3
120
+ when :<<, :>>
121
+ return 4
122
+ when :>, :>=, :<, :<=, :==, :===, :!=
123
+ return 5
124
+ when :undef
125
+ return 6
126
+ else
127
+ raise ArgumentError, "Unknown op: #{@op}"
128
+ end
129
+ end
130
+ end
131
+
132
+ class Prefix < Expression
133
+ def initialize(pc, op, expr)
134
+ super(pc)
135
+ @op = op
136
+ @expr = expr
137
+ end
138
+
139
+ def to_s
140
+ op = @op.to_s
141
+ op.chop! if op[-1] == ?@
142
+ if @op == '!'.intern and @expr.is_a?(Infix) and @expr.op == :== then
143
+ return "#{@expr.fmt(@expr.lhs)} != #{@expr.fmt(@expr.rhs)}"
144
+ elsif self.precedence < @expr.precedence then
145
+ return "#{op}(#{@expr})"
146
+ else
147
+ return "#{op}#{@expr}"
148
+ end
149
+ end
150
+
151
+ def precedence
152
+ return 1
153
+ end
154
+ end
155
+
156
+ class Send < Expression
157
+ attr_reader :is_assignment
158
+
159
+ def initialize(pc, id, has_receiver, has_parens, receiver, block, splat_last, *args)
160
+ super(pc)
161
+ @id = id
162
+ @is_assignment = id.to_s[-1] == ?=
163
+ @has_receiver = has_receiver
164
+ @has_parens = has_parens
165
+ @receiver = receiver
166
+ @block = block
167
+ @splat_last = splat_last
168
+ @args = args
169
+ end
170
+
171
+ def to_s
172
+ s = ''
173
+ receiver_str = @has_receiver \
174
+ ? "#{@receiver}." \
175
+ : nil
176
+ args = @args.map { |x| x.to_s }
177
+ if @splat_last then
178
+ args[-1] = "*#{@args[-1]}"
179
+ end
180
+ if @is_assignment and args.size == 1 then
181
+ s = "#{receiver_str}#{@id.to_s[0..-2]} = #{args[0]}"
182
+ else
183
+ open = @has_parens ? '(' : ''
184
+ close = @has_parens ? ')' : ''
185
+ s = "#{receiver_str}#{@id}#{open}#{args.join(', ')}#{close}"
186
+ end
187
+ if @block then
188
+ # TODO: this code is duplicated elsewhere
189
+ # TODO: handle block args
190
+ env = Environment.new(@block.local_table)
191
+ @block.bytedecode(env)
192
+ expressions = env.expressions + env.stack
193
+ expressions.sort!
194
+ expressions.map! { |x| x.to_s }
195
+ if expressions.length == 1 and
196
+ expressions[0].is_a?(Literal) and
197
+ expressions[0].value == nil then
198
+ # empty
199
+ else
200
+ s << " { #{expressions.join('; ')} }"
201
+ end
202
+ end
203
+ return s
204
+ end
205
+
206
+ def precedence
207
+ if @has_receiver then
208
+ if @receiver.respond_to?(:precedence) then
209
+ return @receiver.precedence
210
+ end
211
+ end
212
+ return 1
213
+ end
214
+ end
215
+
216
+ class Self < Expression
217
+ def initialize(pc)
218
+ super(pc)
219
+ end
220
+
221
+ def to_s
222
+ return "self"
223
+ end
224
+
225
+ def precedence
226
+ return 1
227
+ end
228
+ end
229
+
230
+ class Hash < Expression
231
+ def initialize(pc, args)
232
+ super(pc)
233
+ @args = args
234
+ end
235
+
236
+ def to_s
237
+ s = '{ '
238
+ a = []
239
+ i = 0
240
+ while i < @args.length do
241
+ a << "#{@args[i]} => #{@args[i + 1]}"
242
+ i += 2
243
+ end
244
+ s << a.join(', ')
245
+ s << ' ' if a.length != 0
246
+ s << '}'
247
+ return s
248
+ end
249
+
250
+ def precedence
251
+ return 1
252
+ end
253
+ end
254
+
255
+ class Array < Expression
256
+ def initialize(pc, args)
257
+ super(pc)
258
+ @args = args
259
+ end
260
+
261
+ def to_s
262
+ s = '[ '
263
+ s << @args.join(', ')
264
+ s << ' ]'
265
+ return s
266
+ end
267
+
268
+ def precedence
269
+ return 1
270
+ end
271
+ end
272
+
273
+ class ConcatArray < Expression
274
+ def initialize(array, splat)
275
+ @array = array
276
+ @splat = splat
277
+ end
278
+
279
+ def to_s
280
+ s = '[ '
281
+ case @array
282
+ when Array then s << @array.args.join(', ')
283
+ when Literal then s << @array.value.join(', ')
284
+ else; raise "Unexpected: #{@array.inspect}"
285
+ end
286
+ s << ', *'
287
+ s << @splat.to_s
288
+ s << ' ]'
289
+ end
290
+
291
+ def precedence
292
+ return 1
293
+ end
294
+ end
295
+
296
+ class Defined < Expression
297
+ def initialize(pc, arg)
298
+ super(pc)
299
+ @arg = arg
300
+ end
301
+
302
+ def to_s
303
+ return "defined?(#{@arg.to_s})"
304
+ end
305
+
306
+ def precedence
307
+ return 1
308
+ end
309
+ end
310
+
311
+ class Variable < Expression
312
+ def initialize(pc, name)
313
+ super(pc)
314
+ @name = name
315
+ end
316
+
317
+ def to_s
318
+ return @name.to_s
319
+ end
320
+
321
+ def precedence
322
+ return 1
323
+ end
324
+ end
325
+
326
+ class Constant < Expression
327
+ def initialize(pc, klass, name)
328
+ super(pc)
329
+ @klass = klass
330
+ @name = name
331
+ end
332
+
333
+ def to_s
334
+ if @klass then
335
+ if @klass == Object then
336
+ return "::#{@name}"
337
+ else
338
+ return "#{@klass}::#{@name}"
339
+ end
340
+ else
341
+ return "#{@name}"
342
+ end
343
+ end
344
+
345
+ def precedence
346
+ return 1
347
+ end
348
+ end
349
+
350
+ class ConstantAssignment < Constant
351
+ def initialize(pc, klass, name, value)
352
+ super(pc, klass, name)
353
+ @value = value
354
+ end
355
+
356
+ def to_s
357
+ s = super()
358
+ s << " = #{@value}"
359
+ return s
360
+ end
361
+ end
362
+
363
+ class ConcatStrings < Expression
364
+ def initialize(pc, args)
365
+ super(pc)
366
+ @args = args
367
+ end
368
+
369
+ def to_s
370
+ s = "\""
371
+ @args.each do |arg|
372
+ case arg
373
+ when Literal
374
+ case arg.value
375
+ when String then s << arg.value
376
+ else; s << arg.to_s
377
+ end
378
+ else
379
+ s << "\#{#{arg.to_s}}"
380
+ end
381
+ end
382
+ s << "\""
383
+ end
384
+
385
+ def precedence
386
+ return 1
387
+ end
388
+ end
389
+
390
+ class Assignment < Expression
391
+ attr_reader :rhs
392
+
393
+ def initialize(pc, name, rhs)
394
+ super(pc)
395
+ @name = name
396
+ @rhs = rhs
397
+ end
398
+
399
+ def to_s
400
+ return "#{@name} = #{@rhs}"
401
+ end
402
+
403
+ def precedence
404
+ return 5
405
+ end
406
+ end
407
+
408
+ class ToRegexp < Expression
409
+ def initialize(pc, value)
410
+ super(pc)
411
+ @value = value
412
+ end
413
+
414
+ def to_s
415
+ case @value
416
+ when ConcatStrings
417
+ string = @value.to_s
418
+ unstring = string[1..-2]
419
+ return Regexp.compile(unstring).inspect
420
+ else
421
+ return Regexp.compile(@value.to_s).inspect
422
+ end
423
+ end
424
+
425
+ def precedence
426
+ return 1
427
+ end
428
+ end
429
+
430
+ class Throw < Expression
431
+ def initialize(pc, value)
432
+ super(pc)
433
+ @value = value
434
+ end
435
+
436
+ def to_s
437
+ # TODO: not all throws are breaks...
438
+ if not @value or (@value.is_a?(Literal) and @value.value == nil) then
439
+ return "break"
440
+ else
441
+ return "break #{@value}"
442
+ end
443
+ end
444
+
445
+ def precedence
446
+ return 1
447
+ end
448
+ end
449
+ end
450
+
451
+ end # ByteDecoder
452
+
453
+ end # Internal
454
+
455
+ class RubyVM
456
+ class Instruction
457
+ include Internal::ByteDecoder
458
+
459
+ class PUTOBJECT
460
+ def bytedecode(env)
461
+ env.stack.push Expression::Literal.new(env.pc, self.operands[0])
462
+ end
463
+ end
464
+
465
+ INFIX_OPCODES = {
466
+ OPT_PLUS => '+'.intern,
467
+ OPT_MINUS => '-'.intern,
468
+ OPT_MULT => :*,
469
+ OPT_DIV => :/,
470
+ OPT_MOD => :%,
471
+ OPT_LTLT => :<<,
472
+ # OPT_GTGT => :>>,
473
+ OPT_EQ => :==,
474
+ OPT_NEQ => :!=,
475
+ OPT_GT => :>,
476
+ OPT_GE => :>=,
477
+ OPT_LT => :<,
478
+ OPT_LE => :<=,
479
+ }
480
+
481
+ INFIX_OPERATORS = INFIX_OPCODES.values + [ :===, :>> ]
482
+
483
+ INFIX_OPCODES.each do |klass, op|
484
+ klass.class_eval do
485
+ define_method(:bytedecode) do |env|
486
+ rhs = env.stack.pop
487
+ lhs = env.stack.pop
488
+ env.stack.push Expression::Infix.new(env.pc, op, lhs, rhs)
489
+ end
490
+ end
491
+ end
492
+
493
+ PREFIX_OPCODES = {
494
+ OPT_NOT => :!,
495
+ }
496
+
497
+ if defined?(UNDEF)
498
+ PREFIX_OPCODES[UNDEF] = :undef
499
+ end
500
+
501
+ PREFIX_OPERATORS = PREFIX_OPCODES.values + [ :~, :+@, :-@ ]
502
+
503
+ PREFIX_OPCODES.each do |klass, op|
504
+ klass.class_eval do
505
+ define_method(:bytedecode) do |env|
506
+ expr = env.stack.pop
507
+ env.stack.push Expression::Prefix.new(env.pc, op, expr)
508
+ end
509
+ end
510
+ end
511
+
512
+ class TRACE
513
+ def bytedecode(env)
514
+ end
515
+ end
516
+
517
+ class LEAVE
518
+ def bytedecode(env)
519
+ end
520
+ end
521
+
522
+ LITERAL_OPCODES = [
523
+ PUTNIL,
524
+ DUPARRAY,
525
+ PUTSTRING,
526
+ ]
527
+
528
+ LITERAL_OPCODES.each do |klass|
529
+ klass.class_eval do
530
+ define_method(:bytedecode) do |env|
531
+ env.stack.push Expression::Literal.new(env.pc, @operands[0])
532
+ end
533
+ end
534
+ end
535
+
536
+ class SEND
537
+ def bytedecode(env)
538
+ id = @operands[0]
539
+ num_args = @operands[1]
540
+ args = []
541
+ num_args.times do
542
+ args.unshift env.stack.pop
543
+ end
544
+ has_receiver = !flag_set(RubyVM::CALL_FCALL_BIT)
545
+ has_parens = !flag_set(RubyVM::CALL_VCALL_BIT)
546
+ splat_last = flag_set(RubyVM::CALL_ARGS_SPLAT_BIT)
547
+ receiver = env.stack.pop
548
+ block = @operands[2]
549
+ if INFIX_OPERATORS.include?(id) and args.size == 1 then
550
+ env.stack.push Expression::Infix.new(env.pc, id, receiver, args[0])
551
+ elsif PREFIX_OPERATORS.include?(id) and args.size == 0 then
552
+ env.stack.push Expression::Prefix.new(env.pc, id, receiver)
553
+ else
554
+ env.stack.push Expression::Send.new(
555
+ env.pc, id, has_receiver, has_parens, receiver, block, splat_last, *args)
556
+ end
557
+ end
558
+
559
+ def flag_set(flag)
560
+ flags = @operands[3]
561
+ return flags & flag == flag
562
+ end
563
+ end
564
+
565
+ class PUTSELF
566
+ def bytedecode(env)
567
+ env.stack.push Expression::Self.new(env.pc)
568
+ end
569
+ end
570
+
571
+ class NEWHASH
572
+ def bytedecode(env)
573
+ i = @operands[0]
574
+ args = []
575
+ while i > 0 do
576
+ args.unshift env.stack.pop
577
+ i -= 1
578
+ end
579
+ env.stack.push Expression::Hash.new(env.pc, args)
580
+ end
581
+ end
582
+
583
+ class NEWARRAY
584
+ def bytedecode(env)
585
+ i = @operands[0]
586
+ args = []
587
+ while i > 0 do
588
+ args.unshift env.stack.pop
589
+ i -= 1
590
+ end
591
+ env.stack.push Expression::Array.new(env.pc, args)
592
+ end
593
+ end
594
+
595
+ class DEFINED
596
+ def bytedecode(env)
597
+ env.stack.push Expression::Defined.new(env.pc, @operands[1])
598
+ end
599
+ end
600
+
601
+ GET_VARIABLE_OPCODES = [
602
+ GETCLASSVARIABLE,
603
+ GETINSTANCEVARIABLE,
604
+ GETGLOBAL,
605
+ ]
606
+
607
+ GET_VARIABLE_OPCODES.each do |klass|
608
+ klass.class_eval do
609
+ define_method(:bytedecode) do |env|
610
+ env.stack.push Expression::Variable.new(env.pc, @operands[0])
611
+ end
612
+ end
613
+ end
614
+
615
+ SET_VARIABLE_OPCODES = [
616
+ SETCLASSVARIABLE,
617
+ SETINSTANCEVARIABLE,
618
+ SETGLOBAL,
619
+ ]
620
+
621
+ SET_VARIABLE_OPCODES.each do |klass|
622
+ klass.class_eval do
623
+ define_method(:bytedecode) do |env|
624
+ value = env.stack.pop
625
+ env.stack.delete_at(-1) # TODO: dup'd value.. is this right?
626
+ env.stack.push Expression::Assignment.new(env.pc, @operands[0], value)
627
+ end
628
+ end
629
+ end
630
+
631
+ class GETCONSTANT
632
+ def bytedecode(env)
633
+ klass = env.stack.pop
634
+ env.stack.push Expression::Constant.new(env.pc, klass, @operands[0])
635
+ end
636
+ end
637
+
638
+ class SETCONSTANT
639
+ def bytedecode(env)
640
+ klass = env.stack.pop
641
+ value = env.stack.pop
642
+ env.stack.push Expression::ConstantAssignment.new(env.pc, klass, @operands[0], value)
643
+ end
644
+ end
645
+
646
+ class GETSPECIAL
647
+ def bytedecode(env)
648
+ type = @operands[1] >> 1
649
+ type += ?0.ord if type < 10
650
+ env.stack.push Expression::Variable.new(env.pc, "$#{type.chr}")
651
+ end
652
+ end
653
+
654
+ class GETINLINECACHE
655
+ def bytedecode(env)
656
+ env.stack.push nil
657
+ end
658
+ end
659
+
660
+ class SETINLINECACHE
661
+ def bytedecode(env)
662
+ end
663
+ end
664
+
665
+ class NOP
666
+ def bytedecode(env)
667
+ end
668
+ end
669
+
670
+ class TOSTRING
671
+ def bytedecode(env)
672
+ end
673
+ end
674
+
675
+ class CONCATSTRINGS
676
+ def bytedecode(env)
677
+ i = @operands[0]
678
+ args = []
679
+ while i > 0 do
680
+ args.unshift env.stack.pop
681
+ i -= 1
682
+ end
683
+ env.stack.push Expression::ConcatStrings.new(env.pc, args)
684
+ end
685
+ end
686
+
687
+ class TOREGEXP
688
+ def bytedecode(env)
689
+ env.stack.push Expression::ToRegexp.new(env.pc, env.stack.pop)
690
+ end
691
+ end
692
+
693
+ class DUP
694
+ def bytedecode(env)
695
+ arg = env.stack.pop
696
+ env.stack.push arg
697
+ env.stack.push arg
698
+ end
699
+ end
700
+
701
+ class SETLOCAL
702
+ def bytedecode(env)
703
+ idx = env.local_table.size - @operands[0] + 1
704
+ name = env.local_table[idx]
705
+ value = env.stack.pop
706
+ env.stack.push Expression::Assignment.new(env.pc, name, value)
707
+ end
708
+ end
709
+
710
+ class GETLOCAL
711
+ def bytedecode(env)
712
+ idx = env.local_table.size - @operands[0] + 1
713
+ name = env.local_table[idx]
714
+ env.stack.push Expression::Variable.new(env.pc, name)
715
+ end
716
+ end
717
+
718
+ class SETDYNAMIC
719
+ def bytedecode(env)
720
+ idx = env.local_table.size - @operands[0]
721
+ name = env.local_table[idx]
722
+ value = env.stack.pop
723
+ env.stack.push Expression::Assignment.new(env.pc, name, value)
724
+ end
725
+ end
726
+
727
+ class GETDYNAMIC
728
+ def bytedecode(env)
729
+ idx = env.local_table.size - @operands[0]
730
+ name = env.local_table[idx]
731
+ env.stack.push Expression::Variable.new(env.pc, name)
732
+ end
733
+ end
734
+
735
+ class SETN
736
+ # set nth stack entry to stack top
737
+ def bytedecode(env)
738
+ n = @operands[0]
739
+ dest = -(n+1)
740
+ if env.stack[dest].is_a?(Expression) then
741
+ env.remember env.stack[dest]
742
+ end
743
+ env.stack[dest] = env.stack[-1]
744
+ end
745
+ end
746
+
747
+ class TOPN
748
+ # get nth stack entry from stack top
749
+ def bytedecode(env)
750
+ n = @operands[0]
751
+ idx = -(n+1)
752
+ env.stack[idx]
753
+ end
754
+ end
755
+
756
+ class POP
757
+ def bytedecode(env)
758
+ top = env.stack[-1]
759
+ if top.is_a?(Expression::Send) and top.is_assignment then
760
+ # special case - the return value from the assignment gets
761
+ # thrown away and the result is the rhs
762
+ env.remember env.stack[-2]
763
+ env.stack.delete_at(-2)
764
+ end
765
+ env.remember top
766
+ env.stack.pop
767
+ end
768
+ end
769
+
770
+ class THROW
771
+ def bytedecode(env)
772
+ value = env.stack.pop
773
+ env.remember env.stack.pop
774
+ env.stack.push Expression::Throw.new(env.pc, value)
775
+ end
776
+ end
777
+
778
+ class CONCATARRAY
779
+ def bytedecode(env)
780
+ splat = env.stack.pop
781
+ array = env.stack.pop
782
+ env.stack.push Expression::ConcatArray.new(array, splat)
783
+ end
784
+ end
785
+ end
786
+
787
+ class InstructionSequence
788
+ def bytedecode(env, start_pc=0, end_pc=nil, &block)
789
+ self.each(start_pc) do |instruction|
790
+ # p instruction
791
+ instruction.bytedecode(env)
792
+ # p env.stack
793
+ env.advance(instruction.length)
794
+ break if end_pc and env.pc >= end_pc
795
+ break if block and block.call(instruction)
796
+ end
797
+ end
798
+
799
+ def opt_pc
800
+ opt_table = self.arg_opt_table
801
+ return opt_table.length > 0 ? opt_table[-1] : 0
802
+ end
803
+ end
804
+ end
805
+
806
+ if __FILE__ == $0 then
807
+ # def foo; @@foo = 1; end
808
+ # def foo; a = [2, 3]; foo(1, *a); end
809
+ # def foo; not true; end
810
+ # def foo; catch(:foo) { throw :foo; 42 }; end
811
+ # def foo; a ? b : c; end
812
+ # def foo; loop { a = 1; break }; end
813
+ # def foo; ::BAR; end
814
+ # def foo; a != b; end
815
+ # def foo; 1 - 2; end
816
+ # def foo; +a; end
817
+ # def foo; !a; end
818
+ # def foo; a << b; end
819
+ # def foo; a === b; end
820
+ # def foo; []; end
821
+ def foo; foo.bar = 42; end
822
+ # def foo; h = {}; h.default = true; h; end
823
+ # def foo; @FOO; end
824
+ # def foo; $FOO; end
825
+ # def foo; /foo#{bar}/; end
826
+ # def foo; /foo/; end
827
+ # def foo; foo = 1; bar=2; baz=3 end
828
+ # def foo; "foo#{bar}"; end
829
+ # def foo; $`; end
830
+ # def foo; FOO; end
831
+ # def foo; @@foo; end
832
+ # def foo; defined?(FOO); end
833
+ # def foo; [1+1, 2, 3, 4] ; end
834
+ # def foo; { 1 => 2, 3 => 4 }; end
835
+ # def foo; foo(1 + 2 * (3 + 4), 5); end
836
+ # def foo; 1 + 2 * (3 + 4); end
837
+
838
+ n = method(:foo).body
839
+ is = n.body
840
+ puts is.disasm
841
+
842
+ env = Internal::ByteDecoder::Environment.new(is.local_table)
843
+ s = ''
844
+ # puts "local_table = #{is.local_table.inspect}"
845
+ is.each do |i|
846
+ # p i.operand_types, i.operand_names
847
+ # p i #, i.operand_types, i.operand_names
848
+ print i.class, ' '
849
+ a = []
850
+ i.operand_names.each_with_index do |name, idx|
851
+ a << "#{name}(#{i.operand_types[idx]})=#{i.operands[idx].inspect}"
852
+ end
853
+ puts a.join(', ')
854
+ i.bytedecode(env)
855
+ env.advance(i.length)
856
+ # p env.stack
857
+ # p env.stack.map { |x| x.to_s }
858
+ # puts
859
+ end
860
+
861
+ puts "---"
862
+ (env.expressions + env.stack).sort.each do |expr|
863
+ puts expr.to_s
864
+ end
865
+ end
866
+