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