use std::collections::HashMap;
use crate::{
assembly::{encoder::InstructionEncoder, Immediate, Operand},
metadata::{
method::{ExceptionHandler, ExceptionHandlerFlags},
token::Token,
},
Error, Result,
};
#[derive(Debug, Clone)]
enum HandlerKind {
Catch(Token),
Filter,
Finally,
Fault,
}
#[derive(Debug, Clone)]
struct HandlerInfo {
kind: HandlerKind,
start_label: String,
end_label: Option<String>,
filter_label: Option<String>,
}
#[derive(Debug, Clone)]
struct TryBlockInfo {
start_label: String,
end_label: Option<String>,
handlers: Vec<HandlerInfo>,
}
pub struct InstructionAssembler {
encoder: InstructionEncoder,
try_blocks: HashMap<String, TryBlockInfo>,
label_counter: u32,
}
impl InstructionAssembler {
#[must_use]
pub fn new() -> Self {
Self {
encoder: InstructionEncoder::new(),
try_blocks: HashMap::new(),
label_counter: 0,
}
}
pub fn finish(self) -> Result<(Vec<u8>, u16, Vec<ExceptionHandler>)> {
let (bytecode, max_stack, labels) = self.encoder.finalize()?;
let mut handlers = Vec::new();
for try_block in self.try_blocks.values() {
let try_start = labels
.get(&try_block.start_label)
.ok_or_else(|| Error::UndefinedLabel(try_block.start_label.clone()))?;
let try_end_label = try_block
.end_label
.as_ref()
.ok_or_else(|| malformed_error!("Try block has no end label"))?;
let try_end = labels
.get(try_end_label)
.ok_or_else(|| Error::UndefinedLabel(try_end_label.clone()))?;
for handler in &try_block.handlers {
let handler_start = labels
.get(&handler.start_label)
.ok_or_else(|| Error::UndefinedLabel(handler.start_label.clone()))?;
let handler_end_label = handler
.end_label
.as_ref()
.ok_or_else(|| malformed_error!("Handler has no end label"))?;
let handler_end = labels
.get(handler_end_label)
.ok_or_else(|| Error::UndefinedLabel(handler_end_label.clone()))?;
let (flags, filter_offset, class_token) = match &handler.kind {
HandlerKind::Catch(token) => {
(ExceptionHandlerFlags::EXCEPTION, token.value(), None)
}
HandlerKind::Filter => {
let filter_label = handler.filter_label.as_ref().ok_or_else(|| {
malformed_error!("Filter handler has no filter label")
})?;
let filter_offset = labels
.get(filter_label)
.ok_or_else(|| Error::UndefinedLabel(filter_label.clone()))?;
(ExceptionHandlerFlags::FILTER, *filter_offset, None)
}
HandlerKind::Finally => (ExceptionHandlerFlags::FINALLY, 0, None),
HandlerKind::Fault => (ExceptionHandlerFlags::FAULT, 0, None),
};
handlers.push(ExceptionHandler {
flags,
try_offset: *try_start,
try_length: try_end - try_start,
handler_offset: *handler_start,
handler_length: handler_end - handler_start,
handler: class_token,
filter_offset,
});
}
}
Ok((bytecode, max_stack, handlers))
}
#[must_use]
pub fn max_stack_depth(&self) -> u16 {
self.encoder.max_stack_depth()
}
#[must_use]
pub fn current_stack_depth(&self) -> i16 {
self.encoder.current_stack_depth()
}
#[must_use]
pub fn get_label_position(&self, label_name: &str) -> Option<u32> {
self.encoder.get_label_position(label_name)
}
pub fn label(&mut self, name: &str) -> Result<&mut Self> {
self.encoder.define_label(name)?;
Ok(self)
}
fn generate_label(&mut self, prefix: &str) -> String {
self.label_counter += 1;
format!("__{}_{}", prefix, self.label_counter)
}
pub fn try_start(&mut self, name: &str) -> Result<&mut Self> {
if self.try_blocks.contains_key(name) {
return Err(malformed_error!("Try block '{}' already exists", name));
}
let start_label = self.generate_label(&format!("try_{name}_start"));
self.encoder.define_label(&start_label)?;
self.try_blocks.insert(
name.to_string(),
TryBlockInfo {
start_label,
end_label: None,
handlers: Vec::new(),
},
);
Ok(self)
}
pub fn try_end(&mut self, name: &str) -> Result<&mut Self> {
{
let try_block = self
.try_blocks
.get(name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", name))?;
if try_block.end_label.is_some() {
return Err(malformed_error!("Try block '{}' already ended", name));
}
}
let end_label = self.generate_label(&format!("try_{name}_end"));
self.encoder.define_label(&end_label)?;
if let Some(try_block) = self.try_blocks.get_mut(name) {
try_block.end_label = Some(end_label);
}
Ok(self)
}
pub fn catch_start(&mut self, try_name: &str, exception_type: Token) -> Result<&mut Self> {
{
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
if try_block.end_label.is_none() {
return Err(malformed_error!(
"Try block '{}' must be ended before adding handlers",
try_name
));
}
}
let start_label = self.generate_label(&format!("catch_{try_name}"));
self.encoder.define_label(&start_label)?;
self.encoder.set_stack_depth(1);
self.encoder.set_label_stack_depth(&start_label, 1);
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers.push(HandlerInfo {
kind: HandlerKind::Catch(exception_type),
start_label,
end_label: None,
filter_label: None,
});
}
Ok(self)
}
pub fn catch_end(&mut self, try_name: &str) -> Result<&mut Self> {
let handler_idx = {
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
try_block
.handlers
.iter()
.enumerate()
.rev()
.find(|(_, h)| matches!(h.kind, HandlerKind::Catch(_)) && h.end_label.is_none())
.map(|(i, _)| i)
.ok_or_else(|| {
malformed_error!("No active catch handler for try block '{}'", try_name)
})?
};
let end_label = self.generate_label(&format!("catch_{try_name}_end"));
self.encoder.define_label(&end_label)?;
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers[handler_idx].end_label = Some(end_label);
}
Ok(self)
}
pub fn finally_start(&mut self, try_name: &str) -> Result<&mut Self> {
{
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
if try_block.end_label.is_none() {
return Err(malformed_error!(
"Try block '{}' must be ended before adding handlers",
try_name
));
}
}
let start_label = self.generate_label(&format!("finally_{try_name}"));
self.encoder.define_label(&start_label)?;
self.encoder.set_stack_depth(0);
self.encoder.set_label_stack_depth(&start_label, 0);
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers.push(HandlerInfo {
kind: HandlerKind::Finally,
start_label,
end_label: None,
filter_label: None,
});
}
Ok(self)
}
pub fn finally_end(&mut self, try_name: &str) -> Result<&mut Self> {
let handler_idx = {
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
try_block
.handlers
.iter()
.enumerate()
.rev()
.find(|(_, h)| matches!(h.kind, HandlerKind::Finally) && h.end_label.is_none())
.map(|(i, _)| i)
.ok_or_else(|| {
malformed_error!("No active finally handler for try block '{}'", try_name)
})?
};
let end_label = self.generate_label(&format!("finally_{try_name}_end"));
self.encoder.define_label(&end_label)?;
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers[handler_idx].end_label = Some(end_label);
}
Ok(self)
}
pub fn fault_start(&mut self, try_name: &str) -> Result<&mut Self> {
{
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
if try_block.end_label.is_none() {
return Err(malformed_error!(
"Try block '{}' must be ended before adding handlers",
try_name
));
}
}
let start_label = self.generate_label(&format!("fault_{try_name}"));
self.encoder.define_label(&start_label)?;
self.encoder.set_stack_depth(0);
self.encoder.set_label_stack_depth(&start_label, 0);
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers.push(HandlerInfo {
kind: HandlerKind::Fault,
start_label,
end_label: None,
filter_label: None,
});
}
Ok(self)
}
pub fn fault_end(&mut self, try_name: &str) -> Result<&mut Self> {
let handler_idx = {
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
try_block
.handlers
.iter()
.enumerate()
.rev()
.find(|(_, h)| matches!(h.kind, HandlerKind::Fault) && h.end_label.is_none())
.map(|(i, _)| i)
.ok_or_else(|| {
malformed_error!("No active fault handler for try block '{}'", try_name)
})?
};
let end_label = self.generate_label(&format!("fault_{try_name}_end"));
self.encoder.define_label(&end_label)?;
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers[handler_idx].end_label = Some(end_label);
}
Ok(self)
}
pub fn filter_start(&mut self, try_name: &str) -> Result<&mut Self> {
{
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
if try_block.end_label.is_none() {
return Err(malformed_error!(
"Try block '{}' must be ended before adding handlers",
try_name
));
}
}
let filter_label = self.generate_label(&format!("filter_{try_name}"));
self.encoder.define_label(&filter_label)?;
self.encoder.set_stack_depth(1);
self.encoder.set_label_stack_depth(&filter_label, 1);
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers.push(HandlerInfo {
kind: HandlerKind::Filter,
start_label: String::new(), end_label: None,
filter_label: Some(filter_label),
});
}
Ok(self)
}
pub fn filter_handler(&mut self, try_name: &str) -> Result<&mut Self> {
self.encoder.emit_instruction("endfilter", None)?;
let handler_idx = {
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
try_block
.handlers
.iter()
.enumerate()
.rev()
.find(|(_, h)| matches!(h.kind, HandlerKind::Filter) && h.start_label.is_empty())
.map(|(i, _)| i)
.ok_or_else(|| {
malformed_error!("No active filter expression for try block '{}'", try_name)
})?
};
let start_label = self.generate_label(&format!("filter_{try_name}_handler"));
self.encoder.define_label(&start_label)?;
self.encoder.set_stack_depth(1);
self.encoder.set_label_stack_depth(&start_label, 1);
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers[handler_idx].start_label = start_label;
}
Ok(self)
}
pub fn filter_end(&mut self, try_name: &str) -> Result<&mut Self> {
let handler_idx = {
let try_block = self
.try_blocks
.get(try_name)
.ok_or_else(|| malformed_error!("Try block '{}' not found", try_name))?;
try_block
.handlers
.iter()
.enumerate()
.rev()
.find(|(_, h)| {
matches!(h.kind, HandlerKind::Filter)
&& !h.start_label.is_empty()
&& h.end_label.is_none()
})
.map(|(i, _)| i)
.ok_or_else(|| {
malformed_error!("No active filter handler for try block '{}'", try_name)
})?
};
let end_label = self.generate_label(&format!("filter_{try_name}_end"));
self.encoder.define_label(&end_label)?;
if let Some(try_block) = self.try_blocks.get_mut(try_name) {
try_block.handlers[handler_idx].end_label = Some(end_label);
}
Ok(self)
}
#[must_use]
pub fn has_try_block(&self, name: &str) -> bool {
self.try_blocks.contains_key(name)
}
#[must_use]
pub fn handler_count(&self, name: &str) -> usize {
self.try_blocks.get(name).map_or(0, |tb| tb.handlers.len())
}
pub fn nop(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("nop", None)?;
Ok(self)
}
pub fn ret(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ret", None)?;
Ok(self)
}
pub fn ldarg_0(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldarg.0", None)?;
Ok(self)
}
pub fn ldarg_1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldarg.1", None)?;
Ok(self)
}
pub fn ldarg_2(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldarg.2", None)?;
Ok(self)
}
pub fn ldarg_3(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldarg.3", None)?;
Ok(self)
}
pub fn ldarg_s(&mut self, index: u8) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldarg.s", Some(Operand::Immediate(Immediate::UInt8(index))))?;
Ok(self)
}
pub fn ldarg(&mut self, index: i16) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldarg", Some(Operand::Immediate(Immediate::Int16(index))))?;
Ok(self)
}
pub fn starg_s(&mut self, index: u8) -> Result<&mut Self> {
self.encoder
.emit_instruction("starg.s", Some(Operand::Immediate(Immediate::UInt8(index))))?;
Ok(self)
}
pub fn starg(&mut self, index: i16) -> Result<&mut Self> {
self.encoder
.emit_instruction("starg", Some(Operand::Immediate(Immediate::Int16(index))))?;
Ok(self)
}
pub fn ldloc_0(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldloc.0", None)?;
Ok(self)
}
pub fn ldloc_1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldloc.1", None)?;
Ok(self)
}
pub fn ldloc_2(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldloc.2", None)?;
Ok(self)
}
pub fn ldloc_3(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldloc.3", None)?;
Ok(self)
}
pub fn ldloc_s(&mut self, index: u8) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldloc.s", Some(Operand::Immediate(Immediate::UInt8(index))))?;
Ok(self)
}
pub fn ldloc(&mut self, index: i16) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldloc", Some(Operand::Immediate(Immediate::Int16(index))))?;
Ok(self)
}
pub fn stloc_0(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("stloc.0", None)?;
Ok(self)
}
pub fn stloc_1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("stloc.1", None)?;
Ok(self)
}
pub fn stloc_2(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("stloc.2", None)?;
Ok(self)
}
pub fn stloc_3(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("stloc.3", None)?;
Ok(self)
}
pub fn stloc_s(&mut self, index: u8) -> Result<&mut Self> {
self.encoder
.emit_instruction("stloc.s", Some(Operand::Immediate(Immediate::UInt8(index))))?;
Ok(self)
}
pub fn stloc(&mut self, index: i16) -> Result<&mut Self> {
self.encoder
.emit_instruction("stloc", Some(Operand::Immediate(Immediate::Int16(index))))?;
Ok(self)
}
pub fn ldc_i4_m1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.m1", None)?;
Ok(self)
}
pub fn ldc_i4_0(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.0", None)?;
Ok(self)
}
pub fn ldc_i4_1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.1", None)?;
Ok(self)
}
pub fn ldc_i4_2(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.2", None)?;
Ok(self)
}
pub fn ldc_i4_3(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.3", None)?;
Ok(self)
}
pub fn ldc_i4_4(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.4", None)?;
Ok(self)
}
pub fn ldc_i4_5(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.5", None)?;
Ok(self)
}
pub fn ldc_i4_6(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.6", None)?;
Ok(self)
}
pub fn ldc_i4_7(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.7", None)?;
Ok(self)
}
pub fn ldc_i4_8(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldc.i4.8", None)?;
Ok(self)
}
pub fn ldc_i4_s(&mut self, value: i8) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldc.i4.s", Some(Operand::Immediate(Immediate::Int8(value))))?;
Ok(self)
}
pub fn ldc_i4(&mut self, value: i32) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldc.i4", Some(Operand::Immediate(Immediate::Int32(value))))?;
Ok(self)
}
pub fn ldc_i8(&mut self, value: i64) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldc.i8", Some(Operand::Immediate(Immediate::Int64(value))))?;
Ok(self)
}
pub fn ldc_r4(&mut self, value: f32) -> Result<&mut Self> {
self.encoder.emit_instruction(
"ldc.r4",
Some(Operand::Immediate(Immediate::Float32(value))),
)?;
Ok(self)
}
pub fn ldc_r8(&mut self, value: f64) -> Result<&mut Self> {
self.encoder.emit_instruction(
"ldc.r8",
Some(Operand::Immediate(Immediate::Float64(value))),
)?;
Ok(self)
}
pub fn add(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("add", None)?;
Ok(self)
}
pub fn sub(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("sub", None)?;
Ok(self)
}
pub fn mul(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("mul", None)?;
Ok(self)
}
pub fn div(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("div", None)?;
Ok(self)
}
pub fn rem(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("rem", None)?;
Ok(self)
}
pub fn div_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("div.un", None)?;
Ok(self)
}
pub fn rem_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("rem.un", None)?;
Ok(self)
}
pub fn add_ovf(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("add.ovf", None)?;
Ok(self)
}
pub fn add_ovf_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("add.ovf.un", None)?;
Ok(self)
}
pub fn mul_ovf(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("mul.ovf", None)?;
Ok(self)
}
pub fn mul_ovf_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("mul.ovf.un", None)?;
Ok(self)
}
pub fn sub_ovf(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("sub.ovf", None)?;
Ok(self)
}
pub fn sub_ovf_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("sub.ovf.un", None)?;
Ok(self)
}
pub fn br_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("br.s", label)?;
Ok(self)
}
pub fn brfalse_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("brfalse.s", label)?;
Ok(self)
}
pub fn brtrue_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("brtrue.s", label)?;
Ok(self)
}
pub fn beq_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("beq.s", label)?;
Ok(self)
}
pub fn bge_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("bge.s", label)?;
Ok(self)
}
pub fn bgt_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("bgt.s", label)?;
Ok(self)
}
pub fn ble_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("ble.s", label)?;
Ok(self)
}
pub fn blt_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("blt.s", label)?;
Ok(self)
}
pub fn bne_un_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("bne.un.s", label)?;
Ok(self)
}
pub fn call(&mut self, method_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("call", Some(Operand::Token(method_token)))?;
Ok(self)
}
pub fn callvirt(&mut self, method_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("callvirt", Some(Operand::Token(method_token)))?;
Ok(self)
}
pub fn dup(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("dup", None)?;
Ok(self)
}
pub fn pop(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("pop", None)?;
Ok(self)
}
pub fn ldc_i4_const(&mut self, value: i32) -> Result<&mut Self> {
match value {
-1 => self.ldc_i4_m1(),
0 => self.ldc_i4_0(),
1 => self.ldc_i4_1(),
2 => self.ldc_i4_2(),
3 => self.ldc_i4_3(),
4 => self.ldc_i4_4(),
5 => self.ldc_i4_5(),
6 => self.ldc_i4_6(),
7 => self.ldc_i4_7(),
8 => self.ldc_i4_8(),
v if i8::try_from(v).is_ok() => self.ldc_i4_s(
i8::try_from(v).map_err(|_| malformed_error!("Constant value too large for i8"))?,
),
v => self.ldc_i4(v),
}
}
pub fn ldarg_auto(&mut self, index: u16) -> Result<&mut Self> {
match index {
0 => self.ldarg_0(),
1 => self.ldarg_1(),
2 => self.ldarg_2(),
3 => self.ldarg_3(),
i if i <= 255 => self.ldarg_s(
u8::try_from(i).map_err(|_| malformed_error!("Argument index too large for u8"))?,
),
i => self.ldarg(
i16::try_from(i)
.map_err(|_| malformed_error!("Argument index too large for i16"))?,
),
}
}
pub fn stloc_auto(&mut self, index: u16) -> Result<&mut Self> {
match index {
0 => self.stloc_0(),
1 => self.stloc_1(),
2 => self.stloc_2(),
3 => self.stloc_3(),
i if i <= 255 => self.stloc_s(
u8::try_from(i).map_err(|_| malformed_error!("Local index too large for u8"))?,
),
i => self.stloc(
i16::try_from(i).map_err(|_| malformed_error!("Local index too large for i16"))?,
),
}
}
pub fn ldloc_auto(&mut self, index: u16) -> Result<&mut Self> {
match index {
0 => self.ldloc_0(),
1 => self.ldloc_1(),
2 => self.ldloc_2(),
3 => self.ldloc_3(),
i if i <= 255 => self.ldloc_s(
u8::try_from(i).map_err(|_| malformed_error!("Local index too large for u8"))?,
),
i => self.ldloc(
i16::try_from(i).map_err(|_| malformed_error!("Local index too large for i16"))?,
),
}
}
pub fn and(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("and", None)?;
Ok(self)
}
pub fn or(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("or", None)?;
Ok(self)
}
pub fn xor(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("xor", None)?;
Ok(self)
}
pub fn not(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("not", None)?;
Ok(self)
}
pub fn shl(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("shl", None)?;
Ok(self)
}
pub fn shr(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("shr", None)?;
Ok(self)
}
pub fn shr_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("shr.un", None)?;
Ok(self)
}
pub fn neg(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("neg", None)?;
Ok(self)
}
pub fn conv_i1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.i1", None)?;
Ok(self)
}
pub fn conv_i2(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.i2", None)?;
Ok(self)
}
pub fn conv_i4(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.i4", None)?;
Ok(self)
}
pub fn conv_i8(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.i8", None)?;
Ok(self)
}
pub fn conv_r4(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.r4", None)?;
Ok(self)
}
pub fn conv_r8(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.r8", None)?;
Ok(self)
}
pub fn conv_u1(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.u1", None)?;
Ok(self)
}
pub fn conv_u2(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.u2", None)?;
Ok(self)
}
pub fn conv_u4(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.u4", None)?;
Ok(self)
}
pub fn conv_u8(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("conv.u8", None)?;
Ok(self)
}
pub fn ceq(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ceq", None)?;
Ok(self)
}
pub fn cgt(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("cgt", None)?;
Ok(self)
}
pub fn cgt_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("cgt.un", None)?;
Ok(self)
}
pub fn clt(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("clt", None)?;
Ok(self)
}
pub fn clt_un(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("clt.un", None)?;
Ok(self)
}
pub fn ldnull(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldnull", None)?;
Ok(self)
}
pub fn ldstr(&mut self, string_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldstr", Some(Operand::Token(string_token)))?;
Ok(self)
}
pub fn newobj(&mut self, constructor_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("newobj", Some(Operand::Token(constructor_token)))?;
Ok(self)
}
pub fn castclass(&mut self, type_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("castclass", Some(Operand::Token(type_token)))?;
Ok(self)
}
pub fn isinst(&mut self, type_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("isinst", Some(Operand::Token(type_token)))?;
Ok(self)
}
pub fn ldfld(&mut self, field_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldfld", Some(Operand::Token(field_token)))?;
Ok(self)
}
pub fn stfld(&mut self, field_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("stfld", Some(Operand::Token(field_token)))?;
Ok(self)
}
pub fn ldsfld(&mut self, field_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("ldsfld", Some(Operand::Token(field_token)))?;
Ok(self)
}
pub fn stsfld(&mut self, field_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("stsfld", Some(Operand::Token(field_token)))?;
Ok(self)
}
pub fn throw(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("throw", None)?;
Ok(self)
}
pub fn endfinally(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("endfinally", None)?;
Ok(self)
}
pub fn endfilter(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("endfilter", None)?;
Ok(self)
}
pub fn ldelem_i4(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldelem.i4", None)?;
Ok(self)
}
pub fn stelem_i4(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("stelem.i4", None)?;
Ok(self)
}
pub fn ldlen(&mut self) -> Result<&mut Self> {
self.encoder.emit_instruction("ldlen", None)?;
Ok(self)
}
pub fn newarr(&mut self, element_type_token: Token) -> Result<&mut Self> {
self.encoder
.emit_instruction("newarr", Some(Operand::Token(element_type_token)))?;
Ok(self)
}
pub fn br(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("br", label)?;
Ok(self)
}
pub fn brfalse(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("brfalse", label)?;
Ok(self)
}
pub fn brtrue(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("brtrue", label)?;
Ok(self)
}
pub fn leave_s(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("leave.s", label)?;
Ok(self)
}
pub fn leave(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("leave", label)?;
Ok(self)
}
pub fn switch(&mut self, labels: &[&str]) -> Result<&mut Self> {
self.encoder.emit_switch(labels)?;
Ok(self)
}
pub fn beq(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("beq", label)?;
Ok(self)
}
pub fn bge(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("bge", label)?;
Ok(self)
}
pub fn bgt(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("bgt", label)?;
Ok(self)
}
pub fn ble(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("ble", label)?;
Ok(self)
}
pub fn blt(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("blt", label)?;
Ok(self)
}
pub fn bne_un(&mut self, label: &str) -> Result<&mut Self> {
self.encoder.emit_branch("bne.un", label)?;
Ok(self)
}
pub fn ldc_bool(&mut self, value: bool) -> Result<&mut Self> {
if value {
self.ldc_i4_1()
} else {
self.ldc_i4_0()
}
}
pub fn check_null_and_branch(&mut self, arg_index: u16, label: &str) -> Result<&mut Self> {
self.ldarg_auto(arg_index)?.ldnull()?.bne_un_s(label)
}
pub fn compare_args_and_branch(
&mut self,
arg1: u16,
arg2: u16,
label: &str,
) -> Result<&mut Self> {
self.ldarg_auto(arg1)?.ldarg_auto(arg2)?.beq_s(label)
}
}
impl Default for InstructionAssembler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::token::Token;
#[test]
fn test_fluent_api_basic() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.nop()?.ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x00, 0x2A]);
Ok(())
}
#[test]
fn test_arithmetic_method() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?.ldarg_1()?.add()?.ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x02, 0x03, 0x58, 0x2A]);
Ok(())
}
#[test]
fn test_conditional_logic() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.ldc_i4_0()?
.bgt_s("positive")?
.ldc_i4_0()?
.ret()?
.label("positive")?
.ldc_i4_1()?
.ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode.len(), 8); assert_eq!(bytecode[0], 0x02); assert_eq!(bytecode[1], 0x16); assert_eq!(bytecode[2], 0x30);
Ok(())
}
#[test]
fn test_constant_optimization() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldc_i4_const(-1)? .ldc_i4_const(5)? .ldc_i4_const(42)? .ldc_i4_const(1000)?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode[0], 0x15); assert_eq!(bytecode[1], 0x1B); assert_eq!(bytecode[2], 0x1F); assert_eq!(bytecode[3], 42); assert_eq!(bytecode[4], 0x20);
Ok(())
}
#[test]
fn test_argument_optimization() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_auto(0)? .ldarg_auto(1)? .ldarg_auto(5)? .ldarg_auto(500)?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode[0], 0x02); assert_eq!(bytecode[1], 0x03); assert_eq!(bytecode[2], 0x0E); assert_eq!(bytecode[3], 5); assert_eq!(bytecode[4], 0xFE); assert_eq!(bytecode[5], 0x09); assert_eq!(bytecode[6], 244); assert_eq!(bytecode[7], 1);
Ok(())
}
#[test]
fn test_local_variable_operations() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.stloc_0()? .ldloc_0()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x02, 0x0A, 0x06, 0x2A]);
Ok(())
}
#[test]
fn test_stack_operations() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.dup()? .pop()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x02, 0x25, 0x26, 0x2A]);
Ok(())
}
#[test]
fn test_bitwise_operations() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.ldarg_1()?
.and()? .ldarg_0()?
.ldarg_1()?
.or()? .xor()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(
bytecode,
vec![0x02, 0x03, 0x5F, 0x02, 0x03, 0x60, 0x61, 0x2A]
);
Ok(())
}
#[test]
fn test_comparison_operations() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.ldarg_1()?
.ceq()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x02, 0x03, 0xFE, 0x01, 0x2A]);
Ok(())
}
#[test]
fn test_conversion_operations() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.conv_i4()? .conv_r8()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x02, 0x69, 0x6C, 0x2A]);
Ok(())
}
#[test]
fn test_boolean_constants() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldc_bool(true)? .ldc_bool(false)? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x17, 0x16, 0x2A]);
Ok(())
}
#[test]
fn test_null_operations() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldnull()? .ldarg_0()?
.ceq()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode, vec![0x14, 0x02, 0xFE, 0x01, 0x2A]);
Ok(())
}
#[test]
fn test_branch_optimization() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.ldarg_0()? .brfalse("else")? .ldc_i4_1()? .ret()? .label("else")? .ldc_i4_0()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode.len(), 7);
assert_eq!(bytecode[0], 0x02); assert_eq!(bytecode[1], 0x2C);
Ok(())
}
#[test]
fn test_convenience_methods() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.check_null_and_branch(0, "not_null")?
.ldc_i4_0()? .ret()?
.label("not_null")?
.ldc_i4_1()? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode[0], 0x02); assert_eq!(bytecode[1], 0x14); assert_eq!(bytecode[2], 0x33);
Ok(())
}
#[test]
fn test_field_operations_with_tokens() -> Result<()> {
let field_token = Token::new(0x04000001); let mut asm = InstructionAssembler::new();
asm.ldarg_0()?
.ldfld(field_token)? .ret()?;
let (bytecode, _max_stack, _) = asm.finish()?;
assert_eq!(bytecode[0], 0x02); assert_eq!(bytecode[1], 0x7B);
Ok(())
}
#[test]
fn test_try_catch_basic() -> Result<()> {
let mut asm = InstructionAssembler::new();
let exception_type = Token::new(0x01000001);
asm.try_start("try1")?
.nop()? .leave_s("end")?
.try_end("try1")?
.catch_start("try1", exception_type)?
.pop()? .leave_s("end")?
.catch_end("try1")?
.label("end")?
.ldc_i4_0()?
.ret()?;
let (bytecode, max_stack, handlers) = asm.finish()?;
assert_eq!(handlers.len(), 1);
let handler = &handlers[0];
assert!(handler.is_catch());
assert!(!handler.is_finally());
assert!(!handler.is_fault());
assert!(!handler.is_filter());
assert!(handler.try_offset < handler.handler_offset);
assert!(handler.try_length > 0);
assert!(handler.handler_length > 0);
assert_eq!(handler.filter_offset, exception_type.value());
assert!(!bytecode.is_empty());
assert!(max_stack > 0);
Ok(())
}
#[test]
fn test_try_finally_basic() -> Result<()> {
let mut asm = InstructionAssembler::new();
asm.try_start("try1")?
.nop()? .leave_s("end")?
.try_end("try1")?
.finally_start("try1")?
.nop()? .endfinally()?
.finally_end("try1")?
.label("end")?
.ldc_i4_0()?
.ret()?;
let (bytecode, _max_stack, handlers) = asm.finish()?;
assert_eq!(handlers.len(), 1);
let handler = &handlers[0];
assert!(handler.is_finally());
assert!(!handler.is_catch());
assert!(!bytecode.is_empty());
Ok(())
}
#[test]
fn test_nested_try_blocks() -> Result<()> {
let mut asm = InstructionAssembler::new();
let exception_type1 = Token::new(0x01000001);
let exception_type2 = Token::new(0x01000002);
asm.try_start("outer")?
.try_start("inner")?
.nop()?
.leave_s("inner_end")?
.try_end("inner")?
.catch_start("inner", exception_type2)?
.pop()?
.leave_s("inner_end")?
.catch_end("inner")?
.label("inner_end")?
.leave_s("outer_end")?
.try_end("outer")?
.catch_start("outer", exception_type1)?
.pop()?
.leave_s("outer_end")?
.catch_end("outer")?
.label("outer_end")?
.ldc_i4_0()?
.ret()?;
let (_bytecode, _max_stack, handlers) = asm.finish()?;
assert_eq!(handlers.len(), 2);
for handler in &handlers {
assert!(handler.is_catch());
}
Ok(())
}
#[test]
fn test_multiple_handlers_same_try() -> Result<()> {
let mut asm = InstructionAssembler::new();
let exception_type1 = Token::new(0x01000001);
let exception_type2 = Token::new(0x01000002);
asm.try_start("try1")?
.nop()?
.leave_s("end")?
.try_end("try1")?
.catch_start("try1", exception_type1)?
.pop()?
.leave_s("end")?
.catch_end("try1")?
.catch_start("try1", exception_type2)?
.pop()?
.leave_s("end")?
.catch_end("try1")?
.label("end")?
.ldc_i4_0()?
.ret()?;
let (_bytecode, _max_stack, handlers) = asm.finish()?;
assert_eq!(handlers.len(), 2);
assert!(handlers[0].is_catch());
assert!(handlers[1].is_catch());
assert_eq!(handlers[0].try_offset, handlers[1].try_offset);
assert_eq!(handlers[0].try_length, handlers[1].try_length);
Ok(())
}
#[test]
fn test_has_try_block() -> Result<()> {
let mut asm = InstructionAssembler::new();
assert!(!asm.has_try_block("test"));
asm.try_start("test")?;
assert!(asm.has_try_block("test"));
assert!(!asm.has_try_block("other"));
Ok(())
}
#[test]
fn test_handler_count() -> Result<()> {
let mut asm = InstructionAssembler::new();
let exception_type = Token::new(0x01000001);
assert_eq!(asm.handler_count("test"), 0);
asm.try_start("test")?
.nop()?
.leave_s("end")?
.try_end("test")?;
assert_eq!(asm.handler_count("test"), 0);
asm.catch_start("test", exception_type)?
.pop()?
.leave_s("end")?
.catch_end("test")?;
assert_eq!(asm.handler_count("test"), 1);
asm.finally_start("test")?
.nop()?
.endfinally()?
.finally_end("test")?
.label("end")?
.ldc_i4_0()?
.ret()?;
assert_eq!(asm.handler_count("test"), 2);
Ok(())
}
#[test]
fn test_try_block_errors() -> Result<()> {
let mut asm = InstructionAssembler::new();
let exception_type = Token::new(0x01000001);
asm.try_start("test")?.ldarg_0()?;
assert!(asm.catch_start("test", exception_type).is_err());
assert!(asm.try_end("nonexistent").is_err());
Ok(())
}
}