Skip to main content

ferro_babe/format/
ferro.rs

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}