1#![doc = include_str!("readme.md")]
2use core::range::Range;
3use oak_core::source::{SourceBuffer, ToSource};
4#[cfg(feature = "oak-pretty-print")]
5use oak_pretty_print::{AsDocument, Document};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct MsilRoot {
13 pub items: Vec<Item>,
15}
16
17impl ToSource for MsilRoot {
18 fn to_source(&self, buffer: &mut SourceBuffer) {
19 for item in &self.items {
20 item.to_source(buffer);
21 buffer.push("\n")
22 }
23 }
24}
25
26#[cfg(feature = "oak-pretty-print")]
27impl AsDocument for MsilRoot {
28 fn as_document(&self) -> Document<'_> {
29 Document::join(self.items.iter().map(|i| i.as_document()), Document::Line)
30 }
31}
32
33#[derive(Clone, Debug, PartialEq, Eq, Hash)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub enum Item {
37 Assembly(Assembly),
39 Module(String),
41 Class(Class),
43 AssemblyExtern(String),
45}
46
47impl ToSource for Item {
48 fn to_source(&self, buffer: &mut SourceBuffer) {
49 match self {
50 Item::Assembly(a) => a.to_source(buffer),
51 Item::Module(m) => {
52 buffer.push(".module ");
53 buffer.push(m)
54 }
55 Item::Class(c) => c.to_source(buffer),
56 Item::AssemblyExtern(a) => {
57 buffer.push(".assembly extern ");
58 buffer.push(a);
59 buffer.push(" {}")
60 }
61 }
62 }
63}
64
65#[cfg(feature = "oak-pretty-print")]
66impl AsDocument for Item {
67 fn as_document(&self) -> Document<'_> {
68 match self {
69 Item::Assembly(a) => a.as_document(),
70 Item::Module(m) => Document::Text(format!(".module {}", m).into()),
71 Item::Class(c) => c.as_document(),
72 Item::AssemblyExtern(a) => Document::Text(format!(".assembly extern {} {{}}", a).into()),
73 }
74 }
75}
76
77#[derive(Clone, Debug, PartialEq, Eq, Hash)]
79#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
80pub struct Assembly {
81 pub name: String,
82 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
83 pub span: Range<usize>,
84}
85
86impl ToSource for Assembly {
87 fn to_source(&self, buffer: &mut SourceBuffer) {
88 buffer.push(".assembly ");
89 buffer.push(&self.name);
90 buffer.push(" {}")
91 }
92}
93
94#[cfg(feature = "oak-pretty-print")]
95impl AsDocument for Assembly {
96 fn as_document(&self) -> Document<'_> {
97 Document::Text(format!(".assembly {} {{}}", self.name).into())
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq, Hash)]
103#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
104pub struct Class {
105 pub name: String,
106 pub methods: Vec<Method>,
107 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
108 pub span: Range<usize>,
109}
110
111impl ToSource for Class {
112 fn to_source(&self, buffer: &mut SourceBuffer) {
113 buffer.push(".class public auto ansi beforefieldinit ");
114 buffer.push(&self.name);
115 buffer.push("\n{");
116 for method in &self.methods {
117 buffer.push("\n");
118 method.to_source(buffer)
119 }
120 buffer.push("\n}")
121 }
122}
123
124#[cfg(feature = "oak-pretty-print")]
125impl AsDocument for Class {
126 fn as_document(&self) -> Document<'_> {
127 Document::Concat(vec![
128 Document::Text(format!(".class public auto ansi beforefieldinit {}", self.name).into()),
129 Document::Line,
130 Document::Text("{".into()),
131 Document::indent(Document::join(self.methods.iter().map(|m| m.as_document()), Document::Line)),
132 Document::Text("}".into()),
133 ])
134 }
135}
136
137#[derive(Clone, Debug, PartialEq, Eq, Hash)]
139#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
140pub struct Method {
141 pub name: String,
142 pub instructions: Vec<Instruction>,
143 #[cfg_attr(feature = "serde", serde(with = "oak_core::serde_range"))]
144 pub span: Range<usize>,
145}
146
147impl ToSource for Method {
148 fn to_source(&self, buffer: &mut SourceBuffer) {
149 buffer.push(".method public hidebysig static void ");
150 buffer.push(&self.name);
151 buffer.push("() cil managed\n{");
152 if !self.instructions.is_empty() {
153 buffer.push("\n .entrypoint");
154 for inst in &self.instructions {
155 buffer.push("\n ");
156 inst.to_source(buffer)
157 }
158 }
159 buffer.push("\n}")
160 }
161}
162
163#[cfg(feature = "oak-pretty-print")]
164impl AsDocument for Method {
165 fn as_document(&self) -> Document<'_> {
166 let mut body = vec![Document::Text(".entrypoint".into()), Document::Line];
167 body.extend(self.instructions.iter().map(|i| i.as_document()));
168
169 Document::Concat(vec![
170 Document::Text(format!(".method public hidebysig static void {}() cil managed", self.name).into()),
171 Document::Line,
172 Document::Text("{".into()),
173 Document::indent(Document::join(body, Document::Line)),
174 Document::Text("}".into()),
175 ])
176 }
177}
178
179#[derive(Clone, Debug, PartialEq, Eq, Hash)]
181#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
182pub enum Instruction {
183 Simple(String),
185 String(String),
187 Call(String),
189}
190
191impl ToSource for Instruction {
192 fn to_source(&self, buffer: &mut SourceBuffer) {
193 match self {
194 Instruction::Simple(s) => buffer.push(s),
195 Instruction::String(s) => {
196 buffer.push("ldstr \"");
197 buffer.push(s);
198 buffer.push("\"")
199 }
200 Instruction::Call(s) => {
201 buffer.push("call ");
202 buffer.push(s)
203 }
204 }
205 }
206}
207
208#[cfg(feature = "oak-pretty-print")]
209impl AsDocument for Instruction {
210 fn as_document(&self) -> Document<'_> {
211 match self {
212 Instruction::Simple(s) => Document::Text(s.clone().into()),
213 Instruction::String(s) => Document::Text(format!("ldstr \"{}\"", s).into()),
214 Instruction::Call(s) => Document::Text(format!("call {}", s).into()),
215 }
216 }
217}