Skip to main content

oak_jasm/ast/
mod.rs

1#![doc = include_str!("readme.md")]
2use oak_core::source::{SourceBuffer, ToSource};
3#[cfg(feature = "oak-pretty-print")]
4use oak_pretty_print::{AsDocument, Document};
5use std::{string::String, vec::Vec};
6
7/// JASM root node.
8#[derive(Clone, Debug)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct JasmRoot {
11    /// The class definition.
12    pub class: JasmClass,
13}
14
15impl ToSource for JasmRoot {
16    fn to_source(&self, buffer: &mut SourceBuffer) {
17        self.class.to_source(buffer)
18    }
19}
20
21#[cfg(feature = "oak-pretty-print")]
22impl AsDocument for JasmRoot {
23    fn as_document(&self) -> Document<'_> {
24        self.class.as_document()
25    }
26}
27
28/// AST node for a JASM class declaration.
29#[derive(Clone, Debug)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31pub struct JasmClass {
32    /// Access modifiers (public, private, etc.).
33    pub modifiers: Vec<String>,
34    /// Class name.
35    pub name: String,
36    /// Version information (e.g., 65:0).
37    pub version: Option<String>,
38    /// List of methods.
39    pub methods: Vec<JasmMethod>,
40    /// List of fields.
41    pub fields: Vec<JasmField>,
42    /// Source file information.
43    pub source_file: Option<String>,
44    /// Super class information.
45    pub super_class: Option<String>,
46    /// List of implemented interfaces.
47    pub interfaces: Vec<String>,
48    /// List of annotations.
49    pub annotations: Vec<String>,
50    /// List of attributes.
51    pub attributes: Vec<String>,
52}
53
54impl ToSource for JasmClass {
55    fn to_source(&self, buffer: &mut SourceBuffer) {
56        if let Some(source) = &self.source_file {
57            buffer.push(".source ");
58            buffer.push(source);
59            buffer.push("\n")
60        }
61        if let Some(super_class) = &self.super_class {
62            buffer.push(".super ");
63            buffer.push(super_class);
64            buffer.push("\n")
65        }
66        buffer.push(".class ");
67        for modifier in &self.modifiers {
68            buffer.push(modifier);
69            buffer.push(" ")
70        }
71        buffer.push(&self.name);
72        buffer.push("\n");
73        if let Some(version) = &self.version {
74            buffer.push(".version ");
75            buffer.push(version);
76            buffer.push("\n")
77        }
78        for annotation in &self.annotations {
79            buffer.push(annotation);
80            buffer.push("\n")
81        }
82        for attribute in &self.attributes {
83            buffer.push(attribute);
84            buffer.push("\n")
85        }
86        for interface in &self.interfaces {
87            buffer.push(".interface ");
88            buffer.push(interface);
89            buffer.push("\n")
90        }
91        buffer.push("\n");
92        for field in &self.fields {
93            field.to_source(buffer);
94            buffer.push("\n")
95        }
96        for method in &self.methods {
97            method.to_source(buffer);
98            buffer.push("\n")
99        }
100    }
101}
102
103#[cfg(feature = "oak-pretty-print")]
104impl AsDocument for JasmClass {
105    fn as_document(&self) -> Document<'_> {
106        let mut docs = Vec::new();
107        if let Some(source) = &self.source_file {
108            docs.push(Document::Text(format!(".source {}\n", source).into()))
109        }
110        if let Some(super_class) = &self.super_class {
111            docs.push(Document::Text(format!(".super {}\n", super_class).into()))
112        }
113        let mut class_line = vec![Document::Text(".class ".into())];
114        for modifier in &self.modifiers {
115            class_line.push(Document::Text(modifier.clone().into()));
116            class_line.push(Document::Text(" ".into()))
117        }
118        class_line.push(Document::Text(self.name.clone().into()));
119        docs.push(Document::Concat(class_line));
120        docs.push(Document::Line);
121
122        if let Some(version) = &self.version {
123            docs.push(Document::Text(format!(".version {}\n", version).into()))
124        }
125        for annotation in &self.annotations {
126            docs.push(Document::Text(format!("{}\n", annotation).into()))
127        }
128        for attribute in &self.attributes {
129            docs.push(Document::Text(format!("{}\n", attribute).into()))
130        }
131        for interface in &self.interfaces {
132            docs.push(Document::Text(format!(".interface {}\n", interface).into()))
133        }
134        docs.push(Document::Line);
135
136        for field in &self.fields {
137            docs.push(field.as_document());
138            docs.push(Document::Line)
139        }
140        for method in &self.methods {
141            docs.push(method.as_document());
142            docs.push(Document::Line)
143        }
144        Document::Concat(docs)
145    }
146}
147
148/// AST node for a JASM method declaration.
149#[derive(Clone, Debug)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151pub struct JasmMethod {
152    /// Access modifiers (public, static, etc.).
153    pub modifiers: Vec<String>,
154    /// Method name and type descriptor (e.g., "main":"([Ljava/lang/String)V").
155    pub name_and_descriptor: String,
156    /// Stack size.
157    pub stack_size: Option<u32>,
158    /// Number of local variables.
159    pub locals_count: Option<u32>,
160    /// List of instructions.
161    pub instructions: Vec<JasmInstruction>,
162    /// List of exception handlers.
163    pub exception_handlers: Vec<String>,
164    /// List of annotations.
165    pub annotations: Vec<String>,
166    /// List of attributes.
167    pub attributes: Vec<String>,
168}
169
170impl ToSource for JasmMethod {
171    fn to_source(&self, buffer: &mut SourceBuffer) {
172        buffer.push(".method ");
173        for modifier in &self.modifiers {
174            buffer.push(modifier);
175            buffer.push(" ")
176        }
177        buffer.push(&self.name_and_descriptor);
178        buffer.push("\n");
179        for annotation in &self.annotations {
180            buffer.push("    ");
181            buffer.push(annotation);
182            buffer.push("\n")
183        }
184        for attribute in &self.attributes {
185            buffer.push("    ");
186            buffer.push(attribute);
187            buffer.push("\n")
188        }
189        if let Some(stack) = self.stack_size {
190            buffer.push("    .limit stack ");
191            buffer.push(&stack.to_string());
192            buffer.push("\n")
193        }
194        if let Some(locals) = self.locals_count {
195            buffer.push("    .limit locals ");
196            buffer.push(&locals.to_string());
197            buffer.push("\n")
198        }
199        for exception_handler in &self.exception_handlers {
200            buffer.push("    ");
201            buffer.push(exception_handler);
202            buffer.push("\n")
203        }
204        for inst in &self.instructions {
205            buffer.push("    ");
206            inst.to_source(buffer);
207            buffer.push("\n")
208        }
209        buffer.push(".end method")
210    }
211}
212
213#[cfg(feature = "oak-pretty-print")]
214impl AsDocument for JasmMethod {
215    fn as_document(&self) -> Document<'_> {
216        let mut docs = Vec::new();
217        let mut method_line = vec![Document::Text(".method ".into())];
218        for modifier in &self.modifiers {
219            method_line.push(Document::Text(modifier.clone().into()));
220            method_line.push(Document::Text(" ".into()))
221        }
222        method_line.push(Document::Text(self.name_and_descriptor.clone().into()));
223        docs.push(Document::Concat(method_line));
224        docs.push(Document::Line);
225
226        let mut body = Vec::new();
227        for annotation in &self.annotations {
228            body.push(Document::Text(format!("{}\n", annotation).into()))
229        }
230        for attribute in &self.attributes {
231            body.push(Document::Text(format!("{}\n", attribute).into()))
232        }
233        if let Some(stack) = self.stack_size {
234            body.push(Document::Text(format!(".limit stack {}\n", stack).into()))
235        }
236        if let Some(locals) = self.locals_count {
237            body.push(Document::Text(format!(".limit locals {}\n", locals).into()))
238        }
239        for exception_handler in &self.exception_handlers {
240            body.push(Document::Text(format!("{}\n", exception_handler).into()))
241        }
242        for inst in &self.instructions {
243            body.push(inst.as_document());
244            body.push(Document::Line)
245        }
246
247        docs.push(Document::indent(Document::Concat(body)));
248        docs.push(Document::Text(".end method".into()));
249        Document::Concat(docs)
250    }
251}
252
253/// AST node for a JASM field declaration.
254#[derive(Clone, Debug)]
255#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
256pub struct JasmField {
257    /// Access modifiers (public, static, etc.).
258    pub modifiers: Vec<String>,
259    /// Field name and type descriptor (e.g., "value":"I").
260    pub name_and_descriptor: String,
261    /// List of annotations.
262    pub annotations: Vec<String>,
263    /// List of attributes.
264    pub attributes: Vec<String>,
265}
266
267impl ToSource for JasmField {
268    fn to_source(&self, buffer: &mut SourceBuffer) {
269        for annotation in &self.annotations {
270            buffer.push(annotation);
271            buffer.push("\n");
272            buffer.push("    ")
273        }
274        for attribute in &self.attributes {
275            buffer.push(attribute);
276            buffer.push("\n");
277            buffer.push("    ")
278        }
279        buffer.push(".field ");
280        for modifier in &self.modifiers {
281            buffer.push(modifier);
282            buffer.push(" ")
283        }
284        buffer.push(&self.name_and_descriptor)
285    }
286}
287
288#[cfg(feature = "oak-pretty-print")]
289impl AsDocument for JasmField {
290    fn as_document(&self) -> Document<'_> {
291        let mut docs = Vec::new();
292        for annotation in &self.annotations {
293            docs.push(Document::Text(format!("{}\n", annotation).into()));
294            docs.push(Document::Text("    ".into()))
295        }
296        for attribute in &self.attributes {
297            docs.push(Document::Text(format!("{}\n", attribute).into()));
298            docs.push(Document::Text("    ".into()))
299        }
300        docs.push(Document::Text(".field ".into()));
301        for modifier in &self.modifiers {
302            docs.push(Document::Text(modifier.clone().into()));
303            docs.push(Document::Text(" ".into()))
304        }
305        docs.push(Document::Text(self.name_and_descriptor.clone().into()));
306        Document::Concat(docs)
307    }
308}
309
310/// AST node for a JASM instruction.
311#[derive(Clone, Debug)]
312#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313pub enum JasmInstruction {
314    /// Simple instruction (e.g., aload_0, return).
315    Simple(String),
316    /// Instruction with an argument (e.g., ldc "Hello").
317    WithArgument {
318        /// The instruction name.
319        instruction: String,
320        /// The instruction argument.
321        argument: String,
322    },
323    /// Method call instruction (e.g., invokespecial Method java/lang/Object."<init>":"()V").
324    MethodCall {
325        /// The instruction name.
326        instruction: String,
327        /// The method reference.
328        method_ref: String,
329    },
330    /// Field access instruction (e.g., getstatic Field java/lang/System.out:"Ljava/io/PrintStream;").
331    FieldAccess {
332        /// The instruction name.
333        instruction: String,
334        /// The field reference.
335        field_ref: String,
336    },
337}
338
339impl ToSource for JasmInstruction {
340    fn to_source(&self, buffer: &mut SourceBuffer) {
341        match self {
342            JasmInstruction::Simple(s) => buffer.push(s),
343            JasmInstruction::WithArgument { instruction, argument } => {
344                buffer.push(instruction);
345                buffer.push(" ");
346                buffer.push(argument)
347            }
348            JasmInstruction::MethodCall { instruction, method_ref } => {
349                buffer.push(instruction);
350                buffer.push(" ");
351                buffer.push(method_ref)
352            }
353            JasmInstruction::FieldAccess { instruction, field_ref } => {
354                buffer.push(instruction);
355                buffer.push(" ");
356                buffer.push(field_ref)
357            }
358        }
359    }
360}
361
362#[cfg(feature = "oak-pretty-print")]
363impl AsDocument for JasmInstruction {
364    fn as_document(&self) -> Document<'_> {
365        match self {
366            JasmInstruction::Simple(s) => Document::Text(s.clone().into()),
367            JasmInstruction::WithArgument { instruction, argument } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(argument.clone().into())]),
368            JasmInstruction::MethodCall { instruction, method_ref } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(method_ref.clone().into())]),
369            JasmInstruction::FieldAccess { instruction, field_ref } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(field_ref.clone().into())]),
370        }
371    }
372}