ruby-decompiler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,38 @@
1
+ Ruby-decompiler is a decompiler for ruby code. With it you can...
2
+
3
+ View method signatures:
4
+
5
+ irb(main):015:0> def foo(a, b, *rest, &block); end; method(:foo).signature
6
+ => #<MethodSig::Signature:0x4037093c @origin_class=Object, @arg_info={:b=>"b",
7
+ :block=>"&block", :a=>"a", :rest=>"*rest"}, @name="foo", @arg_names=[:a,
8
+ :b, :rest, :block]>
9
+ irb(main):016:0> proc { |x, y, *rest| }.signature
10
+ => #<Proc::Signature:0x4036cf30 @args=#<Proc::Arguments:0x4036d020 @rest_arg=2,
11
+ @multiple_assignment=true, @names=[:x, :y, :rest]>, @arg_info={:x=>"x", :y=>"y",
12
+ :rest=>"*rest"}>
13
+
14
+ And reconstruct compiled methods:
15
+
16
+ irb(main):001:0> def foo(a, b, *rest, &block)
17
+ irb(main):002:1> begin
18
+ irb(main):003:2* if not a and not b then
19
+ irb(main):004:3* raise "Need more input!"
20
+ irb(main):005:3> end
21
+ irb(main):006:2> return a + b
22
+ irb(main):007:2> ensure
23
+ irb(main):008:2* puts "In ensure block"
24
+ irb(main):009:2> end
25
+ irb(main):010:1> end
26
+ => nil
27
+ irb(main):011:0> m = method(:foo)
28
+ => #<Method: Object#foo>
29
+ irb(main):012:0> puts m.as_code
30
+ def foo(a, b, *rest, &block)
31
+ begin
32
+ (raise("Need more input!")) if (not a and not b)
33
+ return a + b
34
+ ensure
35
+ puts("In ensure block")
36
+ end
37
+ end
38
+ => nil
@@ -0,0 +1,58 @@
1
+ require 'internal/node/as_code'
2
+ require 'internal/vm/iseq/as_code'
3
+ require 'internal/method'
4
+ require 'internal/method/signature'
5
+
6
+ module MethodAsCode
7
+ # Returns a string representation of the method definition/body.
8
+ #
9
+ # irb(main):001:0> def foo(a, b, *rest, &block)
10
+ # irb(main):002:1> begin
11
+ # irb(main):003:2* if not a and not b then
12
+ # irb(main):004:3* raise "Need more input!"
13
+ # irb(main):005:3> end
14
+ # irb(main):006:2> return a + b
15
+ # irb(main):007:2> ensure
16
+ # irb(main):008:2* puts "In ensure block"
17
+ # irb(main):009:2> end
18
+ # irb(main):010:1> end
19
+ # => nil
20
+ # irb(main):011:0> m = method(:foo)
21
+ # => #<Method: Object#foo>
22
+ # irb(main):012:0> puts m.as_code
23
+ # def foo(a, b, *rest, &block)
24
+ # begin
25
+ # (raise("Need more input!")) if (not a and not b)
26
+ # return a + b
27
+ # ensure
28
+ # puts("In ensure block")
29
+ # end
30
+ # end
31
+ #
32
+ def as_code(indent=0, name=nil)
33
+ sig = self.signature
34
+ if self.body.respond_to?(:body) then
35
+ # YARV
36
+ body_expression = self.body.body.as_code(indent+1)
37
+ else
38
+ # pre-YARV
39
+ body_expression = self.body ? self.body.as_code(indent+1) : ''
40
+ end
41
+ name ||= sig.name
42
+ s = "#{' '*indent}def #{name}(#{sig.param_list})\n"
43
+ if body_expression then
44
+ s += "#{body_expression}\n"
45
+ end
46
+ s += "#{' '*indent}end"
47
+ return s
48
+ end
49
+ end
50
+
51
+ class Method
52
+ include MethodAsCode
53
+ end
54
+
55
+ class UnboundMethod
56
+ include MethodAsCode
57
+ end
58
+
@@ -0,0 +1,33 @@
1
+ require 'internal/node/as_expression'
2
+ require 'internal/vm/iseq/as_expression'
3
+ require 'internal/method/signature'
4
+ require 'internal/method'
5
+
6
+ module MethodAsExpression
7
+ # Return a single-line string representation of a method
8
+ # TODO: this method would be more aptly named "as_expression_string".
9
+ def as_expression
10
+ sig = self.signature
11
+ if self.body.respond_to?(:body) then
12
+ # YARV
13
+ body_expression = self.body.body.as_expression
14
+ else
15
+ # pre-YARV
16
+ body_expression = self.body.as_expression
17
+ end
18
+ if body_expression then
19
+ return "def #{sig.name}(#{sig.param_list}); #{body_expression}; end"
20
+ else
21
+ return "def #{sig.name}(#{sig.param_list}); end"
22
+ end
23
+ end
24
+ end
25
+
26
+ class Method
27
+ include MethodAsExpression
28
+ end
29
+
30
+ class UnboundMethod
31
+ include MethodAsExpression
32
+ end
33
+
@@ -0,0 +1,29 @@
1
+ module MethodOrigin
2
+ # An abstraction for a method origin.
3
+ class Origin
4
+ attr_reader :file, :line
5
+
6
+ def initialize(file, line)
7
+ @file = file
8
+ @line = line
9
+ end
10
+
11
+ def to_s
12
+ return "#{file}:#{line}"
13
+ end
14
+ end
15
+
16
+ # Return a Method::Origin representing where the method was defined.
17
+ def origin
18
+ block = body().next
19
+ return Origin.new(block.nd_file, block.nd_line)
20
+ end
21
+ end
22
+
23
+ class Method
24
+ include MethodOrigin
25
+ end
26
+
27
+ class UnboundMethod
28
+ include MethodOrigin
29
+ end
@@ -0,0 +1,147 @@
1
+ require 'internal/method'
2
+ require 'internal/node'
3
+ require 'internal/node/as_expression'
4
+ require 'internal/method/signature/argument'
5
+ require 'internal/method/signature/node'
6
+ require 'internal/method/signature/signature'
7
+
8
+ if defined?(RubyVM) then
9
+ require 'internal/vm/bytedecoder'
10
+ require 'internal/method/signature/iseq'
11
+ end
12
+
13
+ module MethodSig
14
+ # Return the names of the local variables of this method.
15
+ def local_vars
16
+ return self.body.local_vars
17
+ end
18
+
19
+ # Return the names of the arguments this method takes, in the order in
20
+ # which they appear in the argument list.
21
+ def argument_names
22
+ return self.body.argument_names
23
+ end
24
+
25
+ def args_node
26
+ return self.body.args_node
27
+ end
28
+ private :args_node
29
+
30
+ # If this method has a "rest" argument, that is, it has an argument
31
+ # that is preceded by an asterisk (*) in the argument list, then
32
+ # return its index, otherwise return nil.
33
+ def rest_arg
34
+ return self.body.rest_arg
35
+ end
36
+
37
+ # If this method has a "block" argument, that is, it has an argument
38
+ # that is preceded by an ampersand (&) in the argument list, then
39
+ # return its index, otherwise return nil.
40
+ def block_arg
41
+ return self.body.block_arg
42
+ end
43
+
44
+ def set_optional_args(args, args_node, names)
45
+ self.body.set_optional_args(args, args_node, names)
46
+ end
47
+ private :set_optional_args
48
+
49
+ # Return a hash mapping each argument name to a description of that
50
+ # argument.
51
+ def arguments
52
+ names = self.argument_names()
53
+ block_arg = self.block_arg()
54
+
55
+ args = {}
56
+ names.each do |name|
57
+ args[name] = Argument.new(name, false, false)
58
+ end
59
+
60
+ # Optional args
61
+ args_node = args_node()
62
+ set_optional_args(args, args_node, names)
63
+
64
+ # Rest arg
65
+ if self.rest_arg then
66
+ rest_name = names[rest_arg]
67
+ args[rest_name] = Argument.new(rest_name, true, false)
68
+ end
69
+
70
+ # Block arg
71
+ if block_arg then
72
+ block_name = names[block_arg]
73
+ args[block_name] = Argument.new(block_name, false, true)
74
+ end
75
+
76
+ return args
77
+ end
78
+ end
79
+
80
+ class Method
81
+ include MethodSig
82
+
83
+ # Return a String representing the method's signature.
84
+ def signature
85
+ return Signature.new(
86
+ attached_class(),
87
+ method_oid().to_s,
88
+ argument_names(),
89
+ arguments())
90
+ end
91
+ end
92
+
93
+ class UnboundMethod
94
+ include MethodSig
95
+
96
+ # Return a String representing the method's signature.
97
+ def signature
98
+ return Signature.new(
99
+ origin_class(),
100
+ method_oid().to_s,
101
+ argument_names(),
102
+ arguments())
103
+ end
104
+ end
105
+
106
+ if __FILE__ == $0 then
107
+ def foo(); end
108
+ puts method(:foo).signature
109
+
110
+ def foo(foo); end
111
+ puts method(:foo).signature
112
+
113
+ def foo(foo, bar); end
114
+ puts method(:foo).signature
115
+
116
+ def foo(foo=42, bar=10); end
117
+ puts method(:foo).signature
118
+
119
+ def foo(*args); end
120
+ puts method(:foo).signature
121
+
122
+ def foo(foo, bar=42, *args, &block); end
123
+ puts method(:foo).signature
124
+ puts method(:foo).origin
125
+
126
+ def foo(foo, bar=obj.foo(1, 2, foo(10)), *args, &block); end
127
+ puts method(:foo).signature
128
+
129
+ def foo(foo, bar=obj.foo(1 + 1), *args, &block); end
130
+ puts method(:foo).signature
131
+
132
+ def foo(foo, bar=true ? false : 0, *args, &block); end
133
+ puts method(:foo).signature
134
+
135
+ def foo(foo, bar=true, *args, &block); end
136
+ puts method(:foo).signature
137
+
138
+ def foo(foo, bar=nil, *args, &block); end
139
+ puts method(:foo).signature
140
+
141
+ def foo(foo, bar={1=>2}, *args, &block); end
142
+ puts method(:foo).signature
143
+
144
+ def foo(foo, bar=[1,2], *args, &block); end
145
+ puts method(:foo).signature
146
+ end
147
+
@@ -0,0 +1,102 @@
1
+
2
+ module MethodSig
3
+ class Argument
4
+ attr_reader :name
5
+
6
+ def required?
7
+ return !optional?
8
+ end
9
+
10
+ def optional?
11
+ return rest? || block?
12
+ end
13
+
14
+ def rest?
15
+ return @is_rest
16
+ end
17
+
18
+ def block?
19
+ return @is_block
20
+ end
21
+
22
+ def default
23
+ return nil
24
+ end
25
+
26
+ def initialize(name, is_rest, is_block)
27
+ @name = name
28
+ @is_rest = is_rest
29
+ @is_block = is_block
30
+ end
31
+
32
+ def to_s
33
+ if @is_rest then
34
+ prefix = "*"
35
+ elsif @is_block then
36
+ prefix = "&"
37
+ end
38
+
39
+ if self.default then
40
+ suffix = "=#{default()}"
41
+ end
42
+
43
+ return "#{prefix}#{@name}#{suffix}"
44
+ end
45
+ end
46
+
47
+ class OptionalArgument < Argument
48
+ def optional?
49
+ return true
50
+ end
51
+ end
52
+
53
+ class NodeOptionalArgument < OptionalArgument
54
+ attr_reader :node_for_default
55
+ attr_reader :default
56
+
57
+ def initialize(name, default, node_for_default, is_rest, is_block)
58
+ super(name, is_rest, is_block)
59
+ @default = default
60
+ @node_for_default = node_for_default
61
+ end
62
+ end
63
+
64
+ class YarvOptionalArgument < OptionalArgument
65
+ attr_reader :iseq
66
+ attr_reader :pc_start
67
+ attr_reader :local_idx
68
+
69
+ def initialize(name, iseq, pc_start, local_idx, is_rest, is_block)
70
+ super(name, is_rest, is_block)
71
+ @iseq = iseq
72
+ @pc_start = pc_start
73
+ @local_idx = local_idx
74
+ @default = nil
75
+ end
76
+
77
+ def inspect
78
+ default()
79
+ super
80
+ end
81
+
82
+ def default
83
+ return @default if @default
84
+
85
+ env = Internal::ByteDecoder::Environment.new(@iseq.local_table())
86
+ local_table_idx = local_table_idx()
87
+ @iseq.bytedecode(env, @pc_start) do |instr|
88
+ RubyVM::Instruction::SETLOCAL === instr &&
89
+ instr.operands[0] == local_table_idx
90
+ end
91
+ expressions = env.expressions + env.stack
92
+
93
+ @default = expressions[0].rhs.to_s
94
+ return @default
95
+ end
96
+
97
+ def local_table_idx
98
+ return @iseq.local_table.size - @local_idx + 1
99
+ end
100
+ end
101
+ end
102
+
@@ -0,0 +1,52 @@
1
+ class RubyVM
2
+ # YARV 1.9.2 and later
3
+ class InstructionSequence
4
+ include MethodSig
5
+
6
+ def local_vars
7
+ local_vars = self.local_table
8
+ return local_vars
9
+ end
10
+
11
+ def argument_names
12
+ local_vars = self.local_vars
13
+ opt_args = self.arg_opt_table
14
+ opt_args.pop # last arg is a pointer to the start of the code
15
+ num_args = \
16
+ self.argc + \
17
+ opt_args.size + \
18
+ (rest_arg ? 1 : 0) + \
19
+ (block_arg ? 1 : 0)
20
+ return local_vars[0...num_args]
21
+ end
22
+
23
+ def args_node
24
+ return nil
25
+ end
26
+
27
+ def rest_arg
28
+ arg_rest = self.arg_rest
29
+ return arg_rest >= 0 ? arg_rest : nil
30
+ end
31
+
32
+ def block_arg
33
+ arg_block = self.arg_block
34
+ return arg_block >= 0 ? arg_block : nil
35
+ end
36
+
37
+ def set_optional_args(args, args_node, names)
38
+ opt_table = self.arg_opt_table
39
+ opt_table.pop
40
+ first_opt_idx =
41
+ names.size -
42
+ opt_table.size -
43
+ (self.rest_arg ? 1 : 0) -
44
+ (self.block_arg ? 1 : 0)
45
+ opt_table.each_with_index do |pc, idx|
46
+ name = names[first_opt_idx + idx]
47
+ args[name] = YarvOptionalArgument.new(name, self, pc, idx, false, false)
48
+ end
49
+ end
50
+ end
51
+ end
52
+