-
Notifications
You must be signed in to change notification settings - Fork 4
/
instruction.rb
executable file
·253 lines (217 loc) · 6.69 KB
/
instruction.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# -*- coding: cp932 -*-
#
# instruction.rb - structured bytecode library
#
#
module VMLib
class InstSeqTree
Headers = %w(magic major_version minor_version format_type
misc name filename filepath line type locals args exception_table)
# misc name filename type locals args exception_table)
# call-seq:
# VMLib::InstSeqTree.new(parent, iseq)
# parent Partent of InstSeqTree
# For example, when you will construct InstSeqTree of
# the method body, you must 'parent' is InstSeqTree of definition
# code of the method.
# If parent is none, 'parent' is nil.
# iseq Instruction Sequence, Normally the result of
# VM::InstructionSequence.compile(...) or
# VM::InstructionSequence.compile_file(...)
def initialize(parent = nil, iseq = nil, info = [nil, nil, nil])
@info = info
@klasses = {}
@methodes = {}
@blockes = {}
@lblock = {}
@lblock[nil] = []
@lblock_list = [nil]
@header = {}
@body = nil
@parent = parent
@cur_send_no = 0
Headers.each do |name|
@header[name] = nil
end
if iseq then
init_from_ary(iseq.to_a)
end
end
attr :header
attr :klasses
attr :methodes
attr :blockes
attr :lblock
attr :lblock_list
attr :body
attr :parent
attr :info
def init_from_ary(ary)
i = 0
Headers.each do |name|
@header[name] = ary[i]
i = i + 1
end
@body = ary[i]
curlblock = nil
stacktop = nil
@body.each do |inst|
if inst.is_a? Integer then
# Line number
@lblock[curlblock].push inst
elsif inst.is_a? Array
case inst[0]
when :putobject
stacktop = inst[1]
when :defineclass
if inst[2] then
obj = InstSeqTree.new(self, nil, [inst[1], nil, nil])
@klasses[inst[1]] ||= []
obj.init_from_ary(inst[2])
@klasses[inst[1]].push obj
end
when :definemethod
if inst[2] then
obj = InstSeqTree.new(self, nil, [@info[0], inst[1], nil])
obj.init_from_ary(inst[2])
@methodes[inst[1]] = obj
end
when :putiseq
if inst[1] then
obj = InstSeqTree.new(self, nil, [@info[0], stacktop, nil])
obj.init_from_ary(inst[1])
@methodes[stacktop] = obj
end
# inst[3]にはブロック内のメソッドの通し番号が入る
# この通し番号は例外処理でreturn pointの確定などに
# 使われる。
when :send
if inst[3] and inst[3][0] then
obj = InstSeqTree.new(self, nil, [@info[0], @info[1], @cur_send_no])
if inst[3][0] == "YARVInstructionSequence/SimpleDataFormat" then
obj.init_from_ary(inst[3])
else
obj.init_from_ary(inst[3][0])
end
@blockes[@cur_send_no]= obj
inst[3] = [inst[3], @cur_send_no]
else
inst[3] = [nil, @cur_send_no]
end
@cur_send_no += 1
when :invokesuper
if inst[2] then
obj = InstSeqTree.new(self, nil, [@info[0], @info[1], @cur_send_no])
obj.init_from_ary(inst[2])
@blockes[@cur_send_no] = obj
inst[2] = [inst[3], @cur_send_no]
else
inst[2] = [nil, @cur_send_no]
end
@cur_send_no += 1
end
@lblock[curlblock].push inst
elsif inst.is_a? Symbol
# Label
if !@lblock_list.include?(inst) then
@lblock_list.push inst
end
curlblock = inst
@lblock[curlblock] = []
# @lblock[curlblock].push inst
else
raise RuntimeError, inst
end
end
end
def to_a
res = []
Headers.each do |name|
res.push @header[name]
end
stacktop = nil
body = []
clno = Hash.new(0)
@lblock_list.each do |ln|
body.push ln
@lblock[ln].each do |inst|
if inst.is_a? Array then
cinst = inst.clone
case inst[0]
when :putobject
stacktop = inst[1]
when :defineclass
if inst[2] then
cinst[2] = @klasses[inst[1]][clno[inst[1]]].to_a
clno[inst[1]] += 1
end
when :definemethod
if inst[2] then
cinst[2] = @methodes[inst[1]].to_a
end
when :putiseq
if stacktop then
cinst[1] = @methodes[stacktop].to_a
end
when :send
if inst[3] and inst[3][0] then
cinst[3] = @blockes[inst[3][1]].to_a
else
cinst[3] = nil
end
when :invokesuper
if inst[2] and inst[2][0] then
cinst[2] = @blockes[inst[2][1]].to_a
else
cinst[2] = nil
end
end
body.push cinst
else
body.push inst
end
end
end
res.push body
res
end
def merge_other_iseq(oiseq)
oiseq.blockes.each do |name, blk|
@blockes[name] = blk
end
oiseq.methodes.each do |name, mth|
@methodes[name] = mth
end
oiseq.klasses.each do |name, mth|
@klasses[name] = mth
end
self
end
def clear_related_iseq
@blockes = {}
@methodes = {}
@klasses = {}
end
def traverse_code(info, action)
action.call(self, info)
@klasses.each do |name, carray|
linfo = [name, nil, nil, nil]
carray.each do |cont|
cont.traverse_code(linfo, action)
end
end
@methodes.each do |name, cont|
cont.traverse_code([info[0], name, nil, nil], action)
end
@blockes.each do |sno, cont|
cont.traverse_code([info[0], info[1], sno, nil], action)
end
end
def traverse_code_block(info, action)
action.call(self, info)
@blockes.each do |sno, cont|
cont.traverse_code([info[0], info[1], sno, nil], action)
end
end
end
end