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