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 +38 -0
- data/lib/decompiler/method/as_code.rb +58 -0
- data/lib/decompiler/method/as_expression.rb +33 -0
- data/lib/decompiler/method/origin.rb +29 -0
- data/lib/decompiler/method/signature.rb +147 -0
- data/lib/decompiler/method/signature/argument.rb +102 -0
- data/lib/decompiler/method/signature/iseq.rb +52 -0
- data/lib/decompiler/method/signature/node.rb +160 -0
- data/lib/decompiler/method/signature/signature.rb +23 -0
- data/lib/decompiler/module/as_code.rb +45 -0
- data/lib/decompiler/node/as_code.rb +233 -0
- data/lib/decompiler/node/as_expression.rb +619 -0
- data/lib/decompiler/proc/as_code.rb +23 -0
- data/lib/decompiler/proc/as_expression.rb +16 -0
- data/lib/decompiler/proc/signature.rb +184 -0
- data/lib/decompiler/vm/bytedecoder.rb +866 -0
- data/lib/decompiler/vm/iseq/as_code.rb +27 -0
- data/lib/decompiler/vm/iseq/as_expression.rb +26 -0
- data/test/test_as_code.rb +261 -0
- data/test/test_as_expression.rb +229 -0
- data/test/test_methodsig.rb +267 -0
- metadata +105 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
class Node
|
2
|
+
# pre-YARV
|
3
|
+
class SCOPE
|
4
|
+
include MethodSig
|
5
|
+
|
6
|
+
def local_vars
|
7
|
+
return self.tbl || []
|
8
|
+
end
|
9
|
+
|
10
|
+
def argument_names
|
11
|
+
local_vars = self.local_vars
|
12
|
+
args = self.args_node
|
13
|
+
num_required_args = args.cnt
|
14
|
+
num_optional_args = 0
|
15
|
+
opt = args.opt
|
16
|
+
while opt do
|
17
|
+
num_optional_args += 1
|
18
|
+
opt = opt.next
|
19
|
+
end
|
20
|
+
num_args = \
|
21
|
+
num_required_args + \
|
22
|
+
num_optional_args + \
|
23
|
+
(rest_arg ? 1 : 0) + \
|
24
|
+
(block_arg ? 1 : 0)
|
25
|
+
return local_vars[0...num_args]
|
26
|
+
end
|
27
|
+
|
28
|
+
def args_node
|
29
|
+
if self.next.class == Node::ARGS then
|
30
|
+
return self.next
|
31
|
+
elsif self.next.head.class == Node::ARGS then
|
32
|
+
return self.next.head
|
33
|
+
else
|
34
|
+
raise "Could not find method arguments"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def rest_arg
|
39
|
+
args_node = args_node()
|
40
|
+
rest = args_node.rest()
|
41
|
+
if rest.class == Node::LASGN then
|
42
|
+
# subtract 2 to account for implicit vars
|
43
|
+
return rest.cnt - 2
|
44
|
+
elsif not rest
|
45
|
+
return nil
|
46
|
+
else
|
47
|
+
return rest > 0 ? rest - 2 : nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def block_arg
|
52
|
+
block = self.next
|
53
|
+
if block.class == Node::BLOCK and
|
54
|
+
block.next.head.class == Node::BLOCK_ARG then
|
55
|
+
# subtract 2 to account for implicit vars
|
56
|
+
return block.next.head.cnt - 2
|
57
|
+
else
|
58
|
+
return nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_optional_args(args, args_node, names)
|
63
|
+
opt = args_node.opt
|
64
|
+
while opt do
|
65
|
+
head = opt.head
|
66
|
+
if head.class == Node::LASGN then
|
67
|
+
args[head.vid] = NodeOptionalArgument.new(
|
68
|
+
head.vid, head.value.as_expression, head.value, false, false)
|
69
|
+
else
|
70
|
+
raise "Unexpected node type: #{opt.class}"
|
71
|
+
end
|
72
|
+
opt = opt.next
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Pre-YARV, I think
|
78
|
+
class BMETHOD
|
79
|
+
def local_vars
|
80
|
+
raise "TODO: Not implemented yet"
|
81
|
+
end
|
82
|
+
|
83
|
+
def argument_names
|
84
|
+
return self.cval.arguments.names
|
85
|
+
end
|
86
|
+
|
87
|
+
def arguments
|
88
|
+
return self.cval.arguments
|
89
|
+
end
|
90
|
+
|
91
|
+
def args_node
|
92
|
+
raise "TODO: Not implemented yet"
|
93
|
+
end
|
94
|
+
|
95
|
+
def rest_arg
|
96
|
+
raise "TODO: Not implemented yet"
|
97
|
+
end
|
98
|
+
|
99
|
+
def block_arg
|
100
|
+
raise "TODO: Not implemented yet"
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_optional_args(args, args_node, names)
|
104
|
+
raise "TODO: Not implemented yet"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# YARV up to 1.9.1
|
109
|
+
class METHOD
|
110
|
+
include MethodSig
|
111
|
+
|
112
|
+
def local_vars
|
113
|
+
iseq = self.body
|
114
|
+
local_vars = iseq.local_table
|
115
|
+
return local_vars
|
116
|
+
end
|
117
|
+
|
118
|
+
def argument_names
|
119
|
+
local_vars = self.local_vars
|
120
|
+
iseq = self.body
|
121
|
+
opt_args = iseq.arg_opt_table
|
122
|
+
opt_args.pop # last arg is a pointer to the start of the code
|
123
|
+
num_args = \
|
124
|
+
iseq.argc + \
|
125
|
+
opt_args.size + \
|
126
|
+
(rest_arg ? 1 : 0) + \
|
127
|
+
(block_arg ? 1 : 0)
|
128
|
+
return local_vars[0...num_args]
|
129
|
+
end
|
130
|
+
|
131
|
+
def args_node
|
132
|
+
return nil
|
133
|
+
end
|
134
|
+
|
135
|
+
def rest_arg
|
136
|
+
arg_rest = self.body.arg_rest
|
137
|
+
return arg_rest >= 0 ? arg_rest : nil
|
138
|
+
end
|
139
|
+
|
140
|
+
def block_arg
|
141
|
+
arg_block = self.body.arg_block
|
142
|
+
return arg_block >= 0 ? arg_block : nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def set_optional_args(args, args_node, names)
|
146
|
+
opt_table = self.body.arg_opt_table
|
147
|
+
opt_table.pop
|
148
|
+
first_opt_idx =
|
149
|
+
names.size -
|
150
|
+
opt_table.size -
|
151
|
+
(self.rest_arg ? 1 : 0) -
|
152
|
+
(self.block_arg ? 1 : 0)
|
153
|
+
opt_table.each_with_index do |pc, idx|
|
154
|
+
name = names[first_opt_idx + idx]
|
155
|
+
args[name] = YarvOptionalArgument.new(name, self.body, pc, idx, false, false)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module MethodSig
|
2
|
+
# An abstraction for a method signature.
|
3
|
+
class Signature
|
4
|
+
attr_reader :origin_class, :name, :arg_names, :args
|
5
|
+
|
6
|
+
def initialize(origin_class, name, arg_names, args)
|
7
|
+
@origin_class = origin_class
|
8
|
+
@name = name
|
9
|
+
@arg_names = arg_names
|
10
|
+
@args = args
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
return "#{@origin_class}\##{@name}(#{param_list})"
|
15
|
+
end
|
16
|
+
|
17
|
+
def param_list
|
18
|
+
params = @arg_names.map{ |n| args[n].to_s }
|
19
|
+
return params.join(', ')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'internal/node/as_code'
|
2
|
+
require 'internal/module'
|
3
|
+
|
4
|
+
class Module
|
5
|
+
# TODO: it would be nice if we could go back and find the AST
|
6
|
+
# for the class instead of recreating the code from the class's
|
7
|
+
# current state.
|
8
|
+
def as_code(indent=0)
|
9
|
+
imethods = self.instance_methods - self.superclass.instance_methods
|
10
|
+
cmethods = self.instance_methods - self.superclass.instance_methods
|
11
|
+
constants = self.constants - self.superclass.constants
|
12
|
+
name = self.name.gsub(/.*::/, '')
|
13
|
+
|
14
|
+
# TODO: included modules?
|
15
|
+
if self.class == Class then
|
16
|
+
s = "#{' '*indent}class #{name} < #{self.superclass}\n"
|
17
|
+
else
|
18
|
+
s = "#{' '*indent}module #{name}\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
constants.each do |constant|
|
22
|
+
s += "#{' '*indent} #{constant}=#{self.const_get(constant).as_code}\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO: protected/private
|
26
|
+
imethods.each do |method|
|
27
|
+
s += self.instance_method(method).as_code(indent+1)
|
28
|
+
s += "\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
cmethods.each do |method|
|
32
|
+
s += self.instance_method(method).as_code(indent+1, "self.#{method}")
|
33
|
+
s += "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: singleton class constants
|
37
|
+
# TODO: class variables
|
38
|
+
# TODO: singleton instance variables
|
39
|
+
|
40
|
+
s += "#{' '*indent}end"
|
41
|
+
|
42
|
+
return s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require 'internal/node'
|
2
|
+
require 'internal/node/as_expression'
|
3
|
+
require 'internal/object/as_code'
|
4
|
+
require 'rbconfig'
|
5
|
+
|
6
|
+
class Node
|
7
|
+
public
|
8
|
+
|
9
|
+
def as_code(indent=0, *args)
|
10
|
+
return as_code_impl(self, indent, *args)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def as_code_impl(node, indent, *args)
|
16
|
+
# default -- most code is just an expression
|
17
|
+
expression = node.as_expression(*args)
|
18
|
+
if not expression.nil? then
|
19
|
+
return "#{' '*indent}#{expression}"
|
20
|
+
else
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def define_code(klass, &block)
|
27
|
+
if const_defined?(klass) then
|
28
|
+
const_get(klass).instance_eval { define_method(:as_code_impl, &block) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
define_code(:IF) do |node, indent|
|
34
|
+
if node.body.class == Node::BLOCK or
|
35
|
+
node.else.class == Node::BLOCK then
|
36
|
+
s = "#{' '*indent}if #{node.cond.as_expression} then\n"
|
37
|
+
s << "#{' '*indent}#{node.body.as_code(indent+1)}\n"
|
38
|
+
if node.else then
|
39
|
+
s << "#{' '*indent}else\n"
|
40
|
+
s << "#{' '*indent}#{node.else.as_code(indent+1)}\n"
|
41
|
+
end
|
42
|
+
s << "#{' '*indent}end"
|
43
|
+
s
|
44
|
+
else
|
45
|
+
"#{' '*indent}#{node.as_expression}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
define_code(:BLOCK) do |node, indent|
|
50
|
+
a = node.to_a
|
51
|
+
if a[0].class == Node::DASGN_CURR then
|
52
|
+
vardefs = a[0]
|
53
|
+
while vardefs.class == Node::DASGN_CURR do
|
54
|
+
vardefs = vardefs.value
|
55
|
+
end
|
56
|
+
if not vardefs then
|
57
|
+
# ignore variable definitions
|
58
|
+
a.shift
|
59
|
+
end
|
60
|
+
end
|
61
|
+
lines = a.map { |n| n.as_code(indent) }
|
62
|
+
lines.reject! { |e| e.nil? }
|
63
|
+
if lines.size == 0 then
|
64
|
+
"#{' '*indent}nil"
|
65
|
+
else
|
66
|
+
lines.join("\n")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
define_code(:ITER) do |node, indent|
|
71
|
+
"#{' '*indent}#{node.iter.as_expression} {\n" +
|
72
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
73
|
+
"#{' '*indent}}"
|
74
|
+
end
|
75
|
+
|
76
|
+
define_code(:WHILE) do |node, indent|
|
77
|
+
if node.state == 1 then
|
78
|
+
"#{' '*indent}while #{node.cond.as_expression} do\n" +
|
79
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
80
|
+
"#{' '*indent}end"
|
81
|
+
else
|
82
|
+
"#{' '*indent}begin\n" +
|
83
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
84
|
+
"#{' '*indent}end while #{node.cond.as_expression}"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
define_code(:UNTIL) do |node, indent|
|
89
|
+
if node.state == 1 then
|
90
|
+
"#{' '*indent}until #{node.cond.as_expression} do\n" +
|
91
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
92
|
+
"#{' '*indent}end"
|
93
|
+
else
|
94
|
+
"#{' '*indent}begin\n" +
|
95
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
96
|
+
"#{' '*indent}end until #{node.cond.as_expression}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
define_code(:BEGIN) do |node, indent|
|
101
|
+
if node.body.class == Node::RESCUE or
|
102
|
+
node.body.class == Node::ENSURE then
|
103
|
+
s = "#{' '*indent}begin\n" +
|
104
|
+
"#{node.body.as_code(indent+1, true)}\n" +
|
105
|
+
"#{' '*indent}end"
|
106
|
+
elsif node.body then
|
107
|
+
"#{' '*indent}begin\n" +
|
108
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
109
|
+
"#{' '*indent}end"
|
110
|
+
else
|
111
|
+
"#{' '*indent}begin\n" +
|
112
|
+
"#{' '*indent}end\n"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.begin_end(indent, have_begin)
|
117
|
+
s = ''
|
118
|
+
if not have_begin then
|
119
|
+
s << "#{' '*indent}begin\n"
|
120
|
+
indent += 1
|
121
|
+
end
|
122
|
+
yield s, indent, true
|
123
|
+
if not have_begin then
|
124
|
+
indent -= 1
|
125
|
+
s << "\n#{' '*indent}end"
|
126
|
+
end
|
127
|
+
return s
|
128
|
+
end
|
129
|
+
|
130
|
+
define_code(:ENSURE) do |node, indent, *args|
|
131
|
+
begin_ensure = args[0] || false
|
132
|
+
Node.begin_end(indent, begin_ensure) do |s, indent_, begin_ensure|
|
133
|
+
if node.head then
|
134
|
+
s << "#{node.head.as_code(indent_)}\n"
|
135
|
+
end
|
136
|
+
s << "#{' '*(indent_-1)}ensure\n"
|
137
|
+
s << "#{node.ensr.as_code(indent_)}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
define_code(:RESCUE) do |node, indent, *args|
|
142
|
+
begin_rescue = args[0] || false
|
143
|
+
Node.begin_end(indent, begin_rescue) do |s, indent_, begin_rescue|
|
144
|
+
if node.head then
|
145
|
+
if begin_rescue then
|
146
|
+
s << "#{node.head.as_code(indent_)}\n"
|
147
|
+
s << "#{' '*(indent_-1)}rescue #{node.resq.as_code(indent_+1, begin_rescue)}"
|
148
|
+
else
|
149
|
+
s << "#{node.head.as_expression} rescue #{node.resq.as_expression(begin_rescue)}"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
s << "rescue #{node.resq.as_expression(begin_rescue)}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
define_code(:RESBODY) do |node, indent, *args|
|
158
|
+
begin_rescue = args[0] || false
|
159
|
+
if begin_rescue then
|
160
|
+
if node.args then
|
161
|
+
a = node.args.to_a.map { |n| n.as_expression }
|
162
|
+
"#{a.join(', ')}\n" +
|
163
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}"
|
164
|
+
else
|
165
|
+
node.body ? "\n#{' '*indent}#{node.body.as_code(indent+1)}" : ''
|
166
|
+
end
|
167
|
+
else
|
168
|
+
# TODO: assuming node.args is false...
|
169
|
+
node.body ? node.body.as_code : ''
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
define_code(:NEWLINE) do |node, indent|
|
174
|
+
node.next.as_code(indent)
|
175
|
+
end
|
176
|
+
|
177
|
+
define_code(:CASE) do |node, indent|
|
178
|
+
"#{' '*indent}case #{node.head.as_expression}\n" +
|
179
|
+
"#{node.body.as_code(indent)}end"
|
180
|
+
end
|
181
|
+
|
182
|
+
define_code(:WHEN) do |node, indent|
|
183
|
+
args = node.head.to_a.map { |n| n.as_expression }
|
184
|
+
s = "#{' '*indent}when #{args.join(', ')}\n"
|
185
|
+
if node.body then
|
186
|
+
s << "#{' '*indent}#{node.body.as_code(indent+1)}; "
|
187
|
+
end
|
188
|
+
if node.next then
|
189
|
+
s << node.next.as_code
|
190
|
+
end
|
191
|
+
s
|
192
|
+
end
|
193
|
+
|
194
|
+
define_code(:CLASS) do |node, indent|
|
195
|
+
s_super = node.super ? " < #{node.super.as_expression}" : ''
|
196
|
+
if node.respond_to?(:cpath) then
|
197
|
+
path = node.cpath.as_expression
|
198
|
+
else
|
199
|
+
path = node.cname
|
200
|
+
end
|
201
|
+
"#{' '*indent}class #{path}#{s_super}\n" +
|
202
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
203
|
+
"#{' '*indent}end"
|
204
|
+
end
|
205
|
+
|
206
|
+
define_code(:SCLASS) do |node, indent|
|
207
|
+
"#{' '*indent}class << #{node.recv.as_expression}\n" +
|
208
|
+
"#{' '*indent}#{node.body.as_code(indent+1)}\n" +
|
209
|
+
"#{' '*indent}end"
|
210
|
+
end
|
211
|
+
|
212
|
+
define_code(:DEFN) do |node, indent|
|
213
|
+
# TODO: what to do about noex?
|
214
|
+
"#{' '*indent}def #{node.mid}\n" +
|
215
|
+
"#{' '*indent}#{node.next.as_code(indent+1)}\n" +
|
216
|
+
"#{' '*indent}end"
|
217
|
+
end
|
218
|
+
|
219
|
+
define_code(:DEFS) do |node, indent|
|
220
|
+
"#{' '*indent}def #{node.recv.as_expression}.#{node.mid}\n" +
|
221
|
+
"#{' '*indent}#{node.next.as_code(indent+1)}\n" +
|
222
|
+
"#{' '*indent}end"
|
223
|
+
end
|
224
|
+
|
225
|
+
define_code(:SCOPE) do |node, indent|
|
226
|
+
case node.next
|
227
|
+
when nil then ''
|
228
|
+
when Node::ARGS then "#{' '*indent}nil"
|
229
|
+
when Node::BLOCK_ARG then "#{' '*indent}nil"
|
230
|
+
else node.next.as_code(indent)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|