Skip to main content

oak_msil/ast/
mod.rs

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/// MSIL 抽象语法树的根节点
10#[derive(Clone, Debug, PartialEq, Eq, Hash)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub struct MsilRoot {
13    /// 指令、类、方法等项目列表
14    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/// MSIL 中的顶级项目
34#[derive(Clone, Debug, PartialEq, Eq, Hash)]
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub enum Item {
37    /// 程序集定义
38    Assembly(Assembly),
39    /// 模块定义
40    Module(String),
41    /// 类定义
42    Class(Class),
43    /// 外部程序集引用
44    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/// 程序集定义
78#[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/// 类定义
102#[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/// 方法定义
138#[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/// MSIL 指令
180#[derive(Clone, Debug, PartialEq, Eq, Hash)]
181#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
182pub enum Instruction {
183    /// 无参数指令
184    Simple(String),
185    /// 字符串参数指令 (ldstr)
186    String(String),
187    /// 调用指令 (call)
188    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}