use crate::instruction::{Instruction, OpCode};
use super::super::super::super::{HighLevelEmitter, LiteralValue};
const DEFAULT_UNKNOWN_UNPACK_COUNT: usize = 4;
impl HighLevelEmitter {
pub(in super::super::super::super) fn emit_unpack(&mut self, instruction: &Instruction) {
self.push_comment(instruction);
if let Some(value) = self.pop_stack_value() {
if self.try_replay_known_pack(&value) {
return;
}
self.emit_unknown_unpack(instruction, value);
} else {
self.stack_underflow(instruction, 1);
}
}
fn try_replay_known_pack(&mut self, value: &str) -> bool {
let Some(elements) = self.packed_values_by_name.get(value).cloned() else {
return false;
};
for element in elements.iter().rev() {
self.stack.push(element.clone());
}
let count_temp = self.next_temp();
let count = elements.len() as i64;
self.statements.push(format!(
"let {count_temp} = len({value}); // unpack also pushes element count"
));
self.literal_values
.insert(count_temp.clone(), LiteralValue::Integer(count));
self.stack.push(count_temp);
true
}
fn emit_unknown_unpack(&mut self, instruction: &Instruction, value: String) {
let element_count = self.infer_unpack_element_count(instruction);
let elements_temp = self.next_temp();
self.statements.push(format!(
"let {elements_temp} = unpack({value}); // unknown unpack source"
));
for index in 0..element_count {
let element_temp = self.next_temp();
self.statements.push(format!(
"let {element_temp} = unpack_item({elements_temp}, {index}); // synthetic unpack element"
));
self.stack.push(element_temp);
}
let count_temp = self.next_temp();
self.statements.push(format!(
"let {count_temp} = len({value}); // unpack also pushes element count"
));
self.stack.push(count_temp);
}
fn infer_unpack_element_count(&self, instruction: &Instruction) -> usize {
let Some(&unpack_index) = self.index_by_offset.get(&instruction.offset) else {
return DEFAULT_UNKNOWN_UNPACK_COUNT;
};
let mut cursor = unpack_index + 1;
if cursor >= self.program.len() || self.program[cursor].opcode != OpCode::Drop {
return DEFAULT_UNKNOWN_UNPACK_COUNT;
}
cursor += 1;
let mut pops = 0usize;
while cursor < self.program.len() && Self::is_single_pop(self.program[cursor].opcode) {
pops += 1;
cursor += 1;
}
if pops == 0 {
return DEFAULT_UNKNOWN_UNPACK_COUNT;
}
let has_dup_before =
unpack_index > 0 && self.program[unpack_index - 1].opcode == OpCode::Dup;
let count = if has_dup_before {
pops.saturating_sub(1)
} else {
pops
};
if count == 0 {
DEFAULT_UNKNOWN_UNPACK_COUNT
} else {
count
}
}
fn is_single_pop(opcode: OpCode) -> bool {
matches!(
opcode,
OpCode::Drop
| OpCode::Stloc0
| OpCode::Stloc1
| OpCode::Stloc2
| OpCode::Stloc3
| OpCode::Stloc4
| OpCode::Stloc5
| OpCode::Stloc6
| OpCode::Stloc
| OpCode::Starg0
| OpCode::Starg1
| OpCode::Starg2
| OpCode::Starg3
| OpCode::Starg4
| OpCode::Starg5
| OpCode::Starg6
| OpCode::Starg
| OpCode::Stsfld0
| OpCode::Stsfld1
| OpCode::Stsfld2
| OpCode::Stsfld3
| OpCode::Stsfld4
| OpCode::Stsfld5
| OpCode::Stsfld6
| OpCode::Stsfld
)
}
}