ruby-decompiler 0.0.1

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.
@@ -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
+