Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 46ab829

Browse files
committed
Allow calling disasm on instructions
1 parent 7006456 commit 46ab829

File tree

8 files changed

+793
-61
lines changed

8 files changed

+793
-61
lines changed

.rubocop.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Layout/LineLength:
1616
Lint/AmbiguousBlockAssociation:
1717
Enabled: false
1818

19+
Lint/AmbiguousOperatorPrecedence:
20+
Enabled: false
21+
22+
Lint/AmbiguousRange:
23+
Enabled: false
24+
1925
Lint/BooleanSymbol:
2026
Enabled: false
2127

@@ -91,6 +97,9 @@ Style/ExplicitBlockArgument:
9197
Style/FormatString:
9298
Enabled: false
9399

100+
Style/FormatStringToken:
101+
Enabled: false
102+
94103
Style/GuardClause:
95104
Enabled: false
96105

lib/syntax_tree.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
require_relative "syntax_tree/yarv"
3131
require_relative "syntax_tree/yarv/bf"
3232
require_relative "syntax_tree/yarv/compiler"
33+
require_relative "syntax_tree/yarv/disasm_formatter"
3334
require_relative "syntax_tree/yarv/disassembler"
3435
require_relative "syntax_tree/yarv/instruction_sequence"
3536
require_relative "syntax_tree/yarv/instructions"
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# frozen_string_literal: true
2+
3+
module SyntaxTree
4+
module YARV
5+
class DisasmFormatter
6+
attr_reader :output, :queue
7+
attr_reader :current_prefix, :current_iseq
8+
9+
def initialize
10+
@output = StringIO.new
11+
@queue = []
12+
13+
@current_prefix = ""
14+
@current_iseq = nil
15+
end
16+
17+
########################################################################
18+
# Helpers for various instructions
19+
########################################################################
20+
21+
def calldata(value)
22+
flag_names = []
23+
flag_names << :ARGS_SPLAT if value.flag?(CallData::CALL_ARGS_SPLAT)
24+
if value.flag?(CallData::CALL_ARGS_BLOCKARG)
25+
flag_names << :ARGS_BLOCKARG
26+
end
27+
flag_names << :FCALL if value.flag?(CallData::CALL_FCALL)
28+
flag_names << :VCALL if value.flag?(CallData::CALL_VCALL)
29+
flag_names << :ARGS_SIMPLE if value.flag?(CallData::CALL_ARGS_SIMPLE)
30+
flag_names << :BLOCKISEQ if value.flag?(CallData::CALL_BLOCKISEQ)
31+
flag_names << :KWARG if value.flag?(CallData::CALL_KWARG)
32+
flag_names << :KW_SPLAT if value.flag?(CallData::CALL_KW_SPLAT)
33+
flag_names << :TAILCALL if value.flag?(CallData::CALL_TAILCALL)
34+
flag_names << :SUPER if value.flag?(CallData::CALL_SUPER)
35+
flag_names << :ZSUPER if value.flag?(CallData::CALL_ZSUPER)
36+
flag_names << :OPT_SEND if value.flag?(CallData::CALL_OPT_SEND)
37+
flag_names << :KW_SPLAT_MUT if value.flag?(CallData::CALL_KW_SPLAT_MUT)
38+
39+
parts = []
40+
parts << "mid:#{value.method}" if value.method
41+
parts << "argc:#{value.argc}"
42+
parts << "kw:[#{value.kw_arg.join(", ")}]" if value.kw_arg
43+
parts << flag_names.join("|") if flag_names.any?
44+
45+
"<calldata!#{parts.join(", ")}>"
46+
end
47+
48+
def enqueue(iseq)
49+
queue << iseq
50+
end
51+
52+
def event(name)
53+
case name
54+
when :RUBY_EVENT_B_CALL
55+
"Bc"
56+
when :RUBY_EVENT_B_RETURN
57+
"Br"
58+
when :RUBY_EVENT_CALL
59+
"Ca"
60+
when :RUBY_EVENT_CLASS
61+
"Cl"
62+
when :RUBY_EVENT_END
63+
"En"
64+
when :RUBY_EVENT_LINE
65+
"Li"
66+
when :RUBY_EVENT_RETURN
67+
"Re"
68+
else
69+
raise "Unknown event: #{name}"
70+
end
71+
end
72+
73+
def inline_storage(cache)
74+
"<is:#{cache}>"
75+
end
76+
77+
def instruction(name, operands = [])
78+
operands.empty? ? name : "%-38s %s" % [name, operands.join(", ")]
79+
end
80+
81+
def label(value)
82+
value.name["label_".length..]
83+
end
84+
85+
def local(index, explicit: nil, implicit: nil)
86+
current = current_iseq
87+
(explicit || implicit).times { current = current.parent_iseq }
88+
89+
value = "#{current.local_table.name_at(index)}@#{index}"
90+
value << ", #{explicit}" if explicit
91+
value
92+
end
93+
94+
def object(value)
95+
value.inspect
96+
end
97+
98+
########################################################################
99+
# Main entrypoint
100+
########################################################################
101+
102+
def format!
103+
while (@current_iseq = queue.shift)
104+
output << "\n" if output.pos > 0
105+
format_iseq(@current_iseq)
106+
end
107+
108+
output.string
109+
end
110+
111+
private
112+
113+
def format_iseq(iseq)
114+
output << "#{current_prefix}== disasm: "
115+
output << "#<ISeq:#{iseq.name}@<compiled>:1 "
116+
117+
location = iseq.location
118+
output << "(#{location.start_line},#{location.start_column})-"
119+
output << "(#{location.end_line},#{location.end_column})"
120+
output << "> "
121+
122+
if iseq.catch_table.any?
123+
output << "(catch: TRUE)\n"
124+
output << "#{current_prefix}== catch table\n"
125+
126+
with_prefix("#{current_prefix}| ") do
127+
iseq.catch_table.each do |entry|
128+
case entry
129+
when InstructionSequence::CatchBreak
130+
output << "#{current_prefix}catch type: break\n"
131+
format_iseq(entry.iseq)
132+
when InstructionSequence::CatchNext
133+
output << "#{current_prefix}catch type: next\n"
134+
when InstructionSequence::CatchRedo
135+
output << "#{current_prefix}catch type: redo\n"
136+
when InstructionSequence::CatchRescue
137+
output << "#{current_prefix}catch type: rescue\n"
138+
format_iseq(entry.iseq)
139+
end
140+
end
141+
end
142+
143+
output << "#{current_prefix}|#{"-" * 72}\n"
144+
else
145+
output << "(catch: FALSE)\n"
146+
end
147+
148+
if (local_table = iseq.local_table) && !local_table.empty?
149+
output << "#{current_prefix}local table (size: #{local_table.size})\n"
150+
151+
locals =
152+
local_table.locals.each_with_index.map do |local, index|
153+
"[%2d] %s@%d" % [local_table.offset(index), local.name, index]
154+
end
155+
156+
output << "#{current_prefix}#{locals.join(" ")}\n"
157+
end
158+
159+
length = 0
160+
events = []
161+
lines = []
162+
163+
iseq.insns.each do |insn|
164+
case insn
165+
when Integer
166+
lines << insn
167+
when Symbol
168+
events << event(insn)
169+
when InstructionSequence::Label
170+
# skip
171+
else
172+
output << "#{current_prefix}%04d " % length
173+
174+
disasm = insn.disasm(self)
175+
output << disasm
176+
177+
if lines.any?
178+
output << " " * (65 - disasm.length) if disasm.length < 65
179+
elsif events.any?
180+
output << " " * (39 - disasm.length) if disasm.length < 39
181+
end
182+
183+
if lines.any?
184+
output << "(%4d)" % lines.last
185+
lines.clear
186+
end
187+
188+
if events.any?
189+
output << "[#{events.join}]"
190+
events.clear
191+
end
192+
193+
output << "\n"
194+
length += insn.length
195+
end
196+
end
197+
end
198+
199+
def with_prefix(value)
200+
previous = @current_prefix
201+
202+
begin
203+
@current_prefix = value
204+
yield
205+
ensure
206+
@current_prefix = previous
207+
end
208+
end
209+
end
210+
end
211+
end

lib/syntax_tree/yarv/instruction_sequence.rb

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -272,30 +272,9 @@ def to_a
272272
end
273273

274274
def disasm
275-
output = StringIO.new
276-
output << "== disasm: #<ISeq:#{name}@<compiled>:1 (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})> (catch: FALSE)\n"
277-
278-
length = 0
279-
events = []
280-
281-
insns.each do |insn|
282-
case insn
283-
when Integer
284-
# skip
285-
when Symbol
286-
events << insn
287-
when Label
288-
# skip
289-
else
290-
output << "%04d " % length
291-
output << insn.disasm(self)
292-
output << "\n"
293-
end
294-
295-
length += insn.length
296-
end
297-
298-
output.string
275+
formatter = DisasmFormatter.new
276+
formatter.enqueue(self)
277+
formatter.format!
299278
end
300279

301280
# This method converts our linked list of instructions into a final array
@@ -375,7 +354,8 @@ def specialize_instructions!
375354
when Send
376355
calldata = value.calldata
377356

378-
if !value.block_iseq && !calldata.flag?(CallData::CALL_ARGS_BLOCKARG)
357+
if !value.block_iseq &&
358+
!calldata.flag?(CallData::CALL_ARGS_BLOCKARG)
379359
# Specialize the send instruction. If it doesn't have a block
380360
# attached, then we will replace it with an opt_send_without_block
381361
# and do further specializations based on the called method and
@@ -980,8 +960,11 @@ def self.from(source, options = Compiler::Options.new, parent_iseq = nil)
980960

981961
# set up all of the instructions
982962
source[13].each do |insn|
983-
# skip line numbers
984-
next if insn.is_a?(Integer)
963+
# add line numbers
964+
if insn.is_a?(Integer)
965+
iseq.push(insn)
966+
next
967+
end
985968

986969
# add events and labels
987970
if insn.is_a?(Symbol)

0 commit comments

Comments
 (0)