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};
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7use std::{string::String, vec::Vec};
8
9/// JASM root node.
10#[derive(Clone, Debug)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct JasmRoot {
13    pub class: JasmClass,
14}
15
16impl ToSource for JasmRoot {
17    fn to_source(&self, buffer: &mut SourceBuffer) {
18        self.class.to_source(buffer)
19    }
20}
21
22#[cfg(feature = "oak-pretty-print")]
23impl AsDocument for JasmRoot {
24    fn as_document(&self) -> Document<'_> {
25        self.class.as_document()
26    }
27}
28
29/// AST node for a JASM class declaration.
30#[derive(Clone, Debug)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32pub struct JasmClass {
33    /// Access modifiers (public, private, etc.).
34    pub modifiers: Vec<String>,
35    /// Class name.
36    pub name: String,
37    /// Version information (e.g., 65:0).
38    pub version: Option<String>,
39    /// List of methods.
40    pub methods: Vec<JasmMethod>,
41    /// List of fields.
42    pub fields: Vec<JasmField>,
43    /// Source file information.
44    pub source_file: Option<String>,
45}
46
47impl ToSource for JasmClass {
48    fn to_source(&self, buffer: &mut SourceBuffer) {
49        if let Some(source) = &self.source_file {
50            buffer.push(".source ");
51            buffer.push(source);
52            buffer.push("\n")
53        }
54        buffer.push(".class ");
55        for modifier in &self.modifiers {
56            buffer.push(modifier);
57            buffer.push(" ")
58        }
59        buffer.push(&self.name);
60        buffer.push("\n");
61        if let Some(version) = &self.version {
62            buffer.push(".version ");
63            buffer.push(version);
64            buffer.push("\n")
65        }
66        buffer.push("\n");
67        for field in &self.fields {
68            field.to_source(buffer);
69            buffer.push("\n")
70        }
71        for method in &self.methods {
72            method.to_source(buffer);
73            buffer.push("\n")
74        }
75    }
76}
77
78#[cfg(feature = "oak-pretty-print")]
79impl AsDocument for JasmClass {
80    fn as_document(&self) -> Document<'_> {
81        let mut docs = Vec::new();
82        if let Some(source) = &self.source_file {
83            docs.push(Document::Text(format!(".source {}\n", source).into()))
84        }
85        let mut class_line = vec![Document::Text(".class ".into())];
86        for modifier in &self.modifiers {
87            class_line.push(Document::Text(modifier.clone().into()));
88            class_line.push(Document::Text(" ".into()))
89        }
90        class_line.push(Document::Text(self.name.clone().into()));
91        docs.push(Document::Concat(class_line));
92        docs.push(Document::Line);
93
94        if let Some(version) = &self.version {
95            docs.push(Document::Text(format!(".version {}\n", version).into()))
96        }
97        docs.push(Document::Line);
98
99        for field in &self.fields {
100            docs.push(field.as_document());
101            docs.push(Document::Line)
102        }
103        for method in &self.methods {
104            docs.push(method.as_document());
105            docs.push(Document::Line)
106        }
107        Document::Concat(docs)
108    }
109}
110
111/// AST node for a JASM method declaration.
112#[derive(Clone, Debug)]
113#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
114pub struct JasmMethod {
115    /// Access modifiers (public, static, etc.).
116    pub modifiers: Vec<String>,
117    /// Method name and type descriptor (e.g., "main":"([Ljava/lang/String)V").
118    pub name_and_descriptor: String,
119    /// Stack size.
120    pub stack_size: Option<u32>,
121    /// Number of local variables.
122    pub locals_count: Option<u32>,
123    /// List of instructions.
124    pub instructions: Vec<JasmInstruction>,
125}
126
127impl ToSource for JasmMethod {
128    fn to_source(&self, buffer: &mut SourceBuffer) {
129        buffer.push(".method ");
130        for modifier in &self.modifiers {
131            buffer.push(modifier);
132            buffer.push(" ")
133        }
134        buffer.push(&self.name_and_descriptor);
135        buffer.push("\n");
136        if let Some(stack) = self.stack_size {
137            buffer.push("    .limit stack ");
138            buffer.push(&stack.to_string());
139            buffer.push("\n")
140        }
141        if let Some(locals) = self.locals_count {
142            buffer.push("    .limit locals ");
143            buffer.push(&locals.to_string());
144            buffer.push("\n")
145        }
146        for inst in &self.instructions {
147            buffer.push("    ");
148            inst.to_source(buffer);
149            buffer.push("\n")
150        }
151        buffer.push(".end method")
152    }
153}
154
155#[cfg(feature = "oak-pretty-print")]
156impl AsDocument for JasmMethod {
157    fn as_document(&self) -> Document<'_> {
158        let mut docs = Vec::new();
159        let mut method_line = vec![Document::Text(".method ".into())];
160        for modifier in &self.modifiers {
161            method_line.push(Document::Text(modifier.clone().into()));
162            method_line.push(Document::Text(" ".into()))
163        }
164        method_line.push(Document::Text(self.name_and_descriptor.clone().into()));
165        docs.push(Document::Concat(method_line));
166        docs.push(Document::Line);
167
168        let mut body = Vec::new();
169        if let Some(stack) = self.stack_size {
170            body.push(Document::Text(format!(".limit stack {}\n", stack).into()))
171        }
172        if let Some(locals) = self.locals_count {
173            body.push(Document::Text(format!(".limit locals {}\n", locals).into()))
174        }
175        for inst in &self.instructions {
176            body.push(inst.as_document());
177            body.push(Document::Line)
178        }
179
180        docs.push(Document::indent(Document::Concat(body)));
181        docs.push(Document::Text(".end method".into()));
182        Document::Concat(docs)
183    }
184}
185
186/// AST node for a JASM field declaration.
187#[derive(Clone, Debug)]
188#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
189pub struct JasmField {
190    /// Access modifiers (public, static, etc.).
191    pub modifiers: Vec<String>,
192    /// Field name and type descriptor (e.g., "value":"I").
193    pub name_and_descriptor: String,
194}
195
196impl ToSource for JasmField {
197    fn to_source(&self, buffer: &mut SourceBuffer) {
198        buffer.push(".field ");
199        for modifier in &self.modifiers {
200            buffer.push(modifier);
201            buffer.push(" ")
202        }
203        buffer.push(&self.name_and_descriptor)
204    }
205}
206
207#[cfg(feature = "oak-pretty-print")]
208impl AsDocument for JasmField {
209    fn as_document(&self) -> Document<'_> {
210        let mut docs = vec![Document::Text(".field ".into())];
211        for modifier in &self.modifiers {
212            docs.push(Document::Text(modifier.clone().into()));
213            docs.push(Document::Text(" ".into()))
214        }
215        docs.push(Document::Text(self.name_and_descriptor.clone().into()));
216        Document::Concat(docs)
217    }
218}
219
220/// AST node for a JASM instruction.
221#[derive(Clone, Debug)]
222#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
223pub enum JasmInstruction {
224    /// Simple instruction (e.g., aload_0, return).
225    Simple(String),
226    /// Instruction with an argument (e.g., ldc "Hello").
227    WithArgument { instruction: String, argument: String },
228    /// Method call instruction (e.g., invokespecial Method java/lang/Object."<init>":"()V").
229    MethodCall { instruction: String, method_ref: String },
230    /// Field access instruction (e.g., getstatic Field java/lang/System.out:"Ljava/io/PrintStream;").
231    FieldAccess { instruction: String, field_ref: String },
232}
233
234impl ToSource for JasmInstruction {
235    fn to_source(&self, buffer: &mut SourceBuffer) {
236        match self {
237            JasmInstruction::Simple(s) => buffer.push(s),
238            JasmInstruction::WithArgument { instruction, argument } => {
239                buffer.push(instruction);
240                buffer.push(" ");
241                buffer.push(argument)
242            }
243            JasmInstruction::MethodCall { instruction, method_ref } => {
244                buffer.push(instruction);
245                buffer.push(" ");
246                buffer.push(method_ref)
247            }
248            JasmInstruction::FieldAccess { instruction, field_ref } => {
249                buffer.push(instruction);
250                buffer.push(" ");
251                buffer.push(field_ref)
252            }
253        }
254    }
255}
256
257#[cfg(feature = "oak-pretty-print")]
258impl AsDocument for JasmInstruction {
259    fn as_document(&self) -> Document<'_> {
260        match self {
261            JasmInstruction::Simple(s) => Document::Text(s.clone().into()),
262            JasmInstruction::WithArgument { instruction, argument } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(argument.clone().into())]),
263            JasmInstruction::MethodCall { instruction, method_ref } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(method_ref.clone().into())]),
264            JasmInstruction::FieldAccess { instruction, field_ref } => Document::Concat(vec![Document::Text(instruction.clone().into()), Document::Text(" ".into()), Document::Text(field_ref.clone().into())]),
265        }
266    }
267}