1use std::fmt;
2
3use crate::model::{
4 Class, ConstantPoolIndex, ConstantRef, Instruction, InstructionOperand, LdcValueRef,
5 MemberReference,
6};
7
8use super::Formatter;
9use super::opcode::mnemonic;
10
11#[derive(Debug, Default, Clone, Copy)]
12pub struct FerroFormatter;
13
14impl Formatter for FerroFormatter {
15 fn write_class(&self, class: &Class, output: &mut dyn fmt::Write) -> fmt::Result {
16 write!(output, "class {}", class.name())?;
17 if let Some(super_name) = class.super_name() {
18 write!(output, " : {super_name}")?;
19 }
20 write!(output, " ")?;
21 write_flags(output, class.access_flags(), FlagContext::Class)?;
22 writeln!(
23 output,
24 " v{}.{}",
25 class.version().major(),
26 class.version().minor()
27 )?;
28
29 let interfaces: Vec<_> = class.interfaces().collect();
30 if !interfaces.is_empty() {
31 writeln!(output, "implements {}", interfaces.join(", "))?;
32 }
33
34 for field in class.fields() {
35 write!(output, "\nfield {} {} ", field.name(), field.descriptor())?;
36 write_flags(output, field.access_flags(), FlagContext::Field)?;
37 writeln!(output)?;
38 }
39
40 for method in class.methods() {
41 write!(output, "\nmethod {}{} ", method.name(), method.descriptor())?;
42 write_flags(output, method.access_flags(), FlagContext::Method)?;
43
44 if method.has_code() {
45 writeln!(
46 output,
47 " stack={} locals={}",
48 method.max_stack(),
49 method.max_locals()
50 )?;
51
52 if let Some(instructions) = method.instructions() {
53 for instruction in instructions {
54 write_instruction(output, class, instruction)?;
55 }
56 }
57
58 for handler in method.exception_handlers() {
59 write!(
60 output,
61 " catch {:04x}..{:04x} -> {:04x}",
62 handler.start().get(),
63 handler.end().get(),
64 handler.handler().get()
65 )?;
66 if let Some(catch_type) = handler.catch_type() {
67 write!(output, " {catch_type}")?;
68 }
69 writeln!(output)?;
70 }
71 } else {
72 writeln!(output)?;
73 }
74 }
75
76 Ok(())
77 }
78}
79
80fn write_instruction(
81 output: &mut dyn fmt::Write,
82 class: &Class,
83 instruction: Instruction<'_>,
84) -> fmt::Result {
85 write!(
86 output,
87 " {:04x} {:<16}",
88 instruction.offset().get(),
89 mnemonic(instruction.opcode())
90 )?;
91 write_operand(output, class, instruction.operand())?;
92 writeln!(output)
93}
94
95fn write_operand(
96 output: &mut dyn fmt::Write,
97 class: &Class,
98 operand: InstructionOperand<'_>,
99) -> fmt::Result {
100 match operand {
101 InstructionOperand::None => Ok(()),
102 InstructionOperand::Immediate(value) => write!(output, "{value}"),
103 InstructionOperand::Local(index) => write!(output, "{index}"),
104 InstructionOperand::ConstantPool(index) => write_constant(output, class, index),
105 InstructionOperand::Member(reference) => write_member_reference(output, class, reference),
106 InstructionOperand::InvokeInterface { method, count } => {
107 write_constant(output, class, method)?;
108 write!(output, " count={count}")
109 }
110 InstructionOperand::InvokeDynamic { call_site } => write_constant(output, class, call_site),
111 InstructionOperand::Branch { target, .. } => write!(output, "{:04x}", target),
112 InstructionOperand::Ldc(value) => write_ldc_value(output, class, value),
113 InstructionOperand::Increment { local, amount } => write!(output, "{local} {amount}"),
114 InstructionOperand::TableSwitch {
115 default,
116 low,
117 high,
118 targets,
119 base_offset,
120 } => {
121 write!(output, "{low}..{high} default={:04x} [", default.target())?;
122 for (index, target) in targets.iter().enumerate() {
123 if index > 0 {
124 write!(output, ", ")?;
125 }
126 write!(output, "{:04x}", i32::from(base_offset.get()) + target)?;
127 }
128 write!(output, "]")
129 }
130 InstructionOperand::LookupSwitch {
131 default,
132 pairs,
133 base_offset,
134 } => {
135 write!(output, "default={:04x} [", default.target())?;
136 for (index, (key, target)) in pairs.iter().enumerate() {
137 if index > 0 {
138 write!(output, ", ")?;
139 }
140 write!(
141 output,
142 "{key}:{:04x}",
143 i32::from(base_offset.get()) + target
144 )?;
145 }
146 write!(output, "]")
147 }
148 InstructionOperand::MultiArray {
149 class: index,
150 dimensions,
151 } => {
152 write_constant(output, class, index)?;
153 write!(output, " dims={dimensions}")
154 }
155 }
156}
157
158fn write_ldc_value(
159 output: &mut dyn fmt::Write,
160 class: &Class,
161 value: LdcValueRef<'_>,
162) -> fmt::Result {
163 match value {
164 LdcValueRef::ConstantPool(index) => write_constant(output, class, index),
165 LdcValueRef::String(value) => write!(output, "{value:?}"),
166 LdcValueRef::TypeDescriptor => write!(output, "<type>"),
167 LdcValueRef::Integer(value) => write!(output, "{value}"),
168 LdcValueRef::Float(value) => write!(output, "{value}"),
169 LdcValueRef::Long(value) => write!(output, "{value}"),
170 LdcValueRef::Double(value) => write!(output, "{value}"),
171 }
172}
173
174fn write_member_reference(
175 output: &mut dyn fmt::Write,
176 class: &Class,
177 reference: MemberReference<'_>,
178) -> fmt::Result {
179 match reference {
180 MemberReference::ConstantPool(index) => write_constant(output, class, index),
181 MemberReference::Symbolic {
182 owner,
183 name,
184 descriptor,
185 } => write!(output, "{owner}.{name}{descriptor}"),
186 }
187}
188
189fn write_constant(
190 output: &mut dyn fmt::Write,
191 class: &Class,
192 index: ConstantPoolIndex,
193) -> fmt::Result {
194 write!(output, "#{}", index.get())?;
195 match class.constant(index) {
196 Some(ConstantRef::Class { name }) => {
197 if let Some(name) = utf8(class, name) {
198 write!(output, " {name}")?;
199 }
200 }
201 Some(ConstantRef::String { value }) => {
202 if let Some(value) = utf8(class, value) {
203 write!(output, " {value:?}")?;
204 }
205 }
206 Some(ConstantRef::FieldReference {
207 class: owner,
208 name_and_type,
209 })
210 | Some(ConstantRef::MethodReference {
211 class: owner,
212 name_and_type,
213 })
214 | Some(ConstantRef::InterfaceMethodReference {
215 class: owner,
216 name_and_type,
217 }) => write_member_constant(output, class, owner, name_and_type)?,
218 Some(ConstantRef::Integer(value)) => write!(output, " {value}")?,
219 Some(ConstantRef::Float(value)) => write!(output, " {value}")?,
220 Some(ConstantRef::Long(value)) => write!(output, " {value}")?,
221 Some(ConstantRef::Double(value)) => write!(output, " {value}")?,
222 _ => {}
223 }
224 Ok(())
225}
226
227fn write_member_constant(
228 output: &mut dyn fmt::Write,
229 class: &Class,
230 owner: ConstantPoolIndex,
231 name_and_type_index: ConstantPoolIndex,
232) -> fmt::Result {
233 let Some(owner) = class_name(class, owner) else {
234 return Ok(());
235 };
236 let Some((name, descriptor)) = name_and_type(class, name_and_type_index) else {
237 return Ok(());
238 };
239 write!(output, " {owner}.{name}{descriptor}")
240}
241
242fn class_name(class: &Class, index: ConstantPoolIndex) -> Option<&str> {
243 let ConstantRef::Class { name } = class.constant(index)? else {
244 return None;
245 };
246 utf8(class, name)
247}
248
249fn name_and_type(class: &Class, index: ConstantPoolIndex) -> Option<(&str, &str)> {
250 let ConstantRef::NameAndType { name, descriptor } = class.constant(index)? else {
251 return None;
252 };
253 Some((utf8(class, name)?, utf8(class, descriptor)?))
254}
255
256fn utf8(class: &Class, index: ConstantPoolIndex) -> Option<&str> {
257 let ConstantRef::Utf8(value) = class.constant(index)? else {
258 return None;
259 };
260 Some(value)
261}
262
263#[derive(Clone, Copy)]
264enum FlagContext {
265 Class,
266 Field,
267 Method,
268}
269
270fn write_flags(output: &mut dyn fmt::Write, flags: u16, context: FlagContext) -> fmt::Result {
271 write!(output, "[")?;
272 let mut first = true;
273
274 for (bit, name) in flag_names(context) {
275 if flags & bit != 0 {
276 if !first {
277 write!(output, " ")?;
278 }
279 write!(output, "{name}")?;
280 first = false;
281 }
282 }
283
284 write!(output, "]")
285}
286
287fn flag_names(context: FlagContext) -> &'static [(u16, &'static str)] {
288 const CLASS_FLAGS: &[(u16, &str)] = &[
289 (0x0001, "public"),
290 (0x0010, "final"),
291 (0x0020, "super"),
292 (0x0200, "interface"),
293 (0x0400, "abstract"),
294 (0x1000, "synthetic"),
295 (0x2000, "annotation"),
296 (0x4000, "enum"),
297 (0x8000, "module"),
298 ];
299 const FIELD_FLAGS: &[(u16, &str)] = &[
300 (0x0001, "public"),
301 (0x0002, "private"),
302 (0x0004, "protected"),
303 (0x0008, "static"),
304 (0x0010, "final"),
305 (0x0040, "volatile"),
306 (0x0080, "transient"),
307 (0x1000, "synthetic"),
308 (0x4000, "enum"),
309 ];
310 const METHOD_FLAGS: &[(u16, &str)] = &[
311 (0x0001, "public"),
312 (0x0002, "private"),
313 (0x0004, "protected"),
314 (0x0008, "static"),
315 (0x0010, "final"),
316 (0x0020, "synchronized"),
317 (0x0040, "bridge"),
318 (0x0080, "varargs"),
319 (0x0100, "native"),
320 (0x0400, "abstract"),
321 (0x0800, "strict"),
322 (0x1000, "synthetic"),
323 ];
324
325 match context {
326 FlagContext::Class => CLASS_FLAGS,
327 FlagContext::Field => FIELD_FLAGS,
328 FlagContext::Method => METHOD_FLAGS,
329 }
330}