use luac_parser::{LuaChunk, LuaConstant, LuaNumber};
use crate::lua51::ast::*;
use crate::lua51::instruction::{is_k, index_k};
use crate::lua51::opcodes::OpCode;
use super::Lifter;
impl<'a> Lifter<'a> {
pub(super) fn set_reg(&mut self, reg: u32, expr: Expr) {
let r = reg as usize;
if r < self.regs.len() {
self.regs[r] = Some(expr);
}
self.local_names.remove(®);
self.pending_tables.remove(®);
self.capture_aliases.remove(®);
}
pub(super) fn set_reg_local(&mut self, reg: u32, expr: Expr, stmts: &mut Block) {
if reg >= self.num_params && !self.declared_locals.contains(®) {
self.declared_locals.insert(reg);
let name = self.make_local_name_for_expr(reg, &expr);
self.local_names.insert(reg, name.clone());
let r = reg as usize;
if r < self.regs.len() {
self.regs[r] = Some(Expr::Name(name.clone()));
}
stmts.push(Stat::LocalAssign {
names: vec![name],
exprs: vec![expr],
});
} else if let Some(name) = self.local_names.get(®).cloned() {
let r = reg as usize;
if r < self.regs.len() {
self.regs[r] = Some(Expr::Name(name.clone()));
}
stmts.push(Stat::Assign {
targets: vec![Expr::Name(name)],
values: vec![expr],
});
} else {
let r = reg as usize;
if r < self.regs.len() {
self.regs[r] = Some(expr);
}
}
}
pub(super) fn assign_reg_expr(&mut self, pc: usize, reg: u32, expr: Expr, stmts: &mut Block) {
let block_id = self.cfg.block_of(pc);
let live_out_of_block = (reg as usize) < self.liveness.max_reg
&& self.liveness.live_out[block_id][reg as usize];
if self.accumulator_regs.contains(®)
&& reg >= self.num_params
&& live_out_of_block
{
self.set_reg_local(reg, expr, stmts);
} else {
self.set_reg(reg, expr);
}
}
pub(super) fn resolve_closure_upvalues(
&mut self,
pc: usize,
sub_chunk: &LuaChunk,
stmts: &mut Block,
) -> Vec<Option<Expr>> {
let num_upvalues = sub_chunk.num_upvalues as usize;
let mut resolved = Vec::with_capacity(num_upvalues);
for offset in 0..num_upvalues {
let capture_pc = pc + 1 + offset;
if capture_pc >= self.cfg.instructions.len() {
resolved.push(None);
continue;
}
let capture = self.cfg.instructions[capture_pc];
let expr = match capture.op {
OpCode::Move => Some(self.capture_stack_upvalue(capture.b(), capture_pc, stmts)),
OpCode::GetUpval => Some(self.upvalue_expr(capture.b())),
_ => None,
};
resolved.push(expr);
}
resolved
}
fn capture_stack_upvalue(&mut self, reg: u32, pc: usize, stmts: &mut Block) -> Expr {
if let Some(name) = self.local_names.get(®).cloned() {
return Expr::Name(name);
}
if reg < self.num_params {
let name = self.local_name(reg, pc);
self.local_names.insert(reg, name.clone());
let r = reg as usize;
if r < self.regs.len() {
self.regs[r] = Some(Expr::Name(name.clone()));
}
return Expr::Name(name);
}
if let Some(alias) = self.capture_aliases.get(®).cloned() {
return alias;
}
match self.reg_expr(reg) {
Expr::Name(name) => Expr::Name(name),
Expr::Global(name) => Expr::Global(name),
Expr::Upvalue(idx) => self.upvalue_expr(idx),
Expr::Field(table, field) => Expr::Field(table, field),
expr => {
let name = self.local_name(reg, pc);
self.local_names.insert(reg, name.clone());
self.declared_locals.insert(reg);
let r = reg as usize;
if r < self.regs.len() {
self.regs[r] = Some(Expr::Name(name.clone()));
}
self.pending_tables.remove(®);
self.capture_aliases.remove(®);
stmts.push(Stat::LocalAssign {
names: vec![name.clone()],
exprs: vec![expr],
});
Expr::Name(name)
}
}
}
pub(super) fn reg_expr(&self, reg: u32) -> Expr {
if let Some(name) = self.local_names.get(®) {
return Expr::Name(name.clone());
}
if let Some(fields) = self.pending_tables.get(®) {
if !fields.is_empty() {
return Expr::Table(fields.clone());
}
}
let r = reg as usize;
if r < self.regs.len() {
self.regs[r].clone().unwrap_or(Expr::Register(reg))
} else {
Expr::Register(reg)
}
}
pub(super) fn rk_expr(&self, rk: u32) -> Expr {
if is_k(rk) {
self.const_expr(index_k(rk))
} else {
self.reg_expr(rk)
}
}
pub(super) fn const_expr(&self, idx: u32) -> Expr {
let i = idx as usize;
if i >= self.chunk.constants.len() {
return Expr::Nil;
}
match &self.chunk.constants[i] {
LuaConstant::Null => Expr::Nil,
LuaConstant::Bool(b) => Expr::Bool(*b),
LuaConstant::Number(n) => match n {
LuaNumber::Integer(v) => Expr::Number(NumLit::Int(*v)),
LuaNumber::Float(v) => Expr::Number(NumLit::Float(*v)),
},
LuaConstant::String(s) => Expr::StringLit(s.as_ref().to_vec()),
_ => Expr::Nil,
}
}
pub(super) fn const_string(&self, idx: u32) -> String {
let i = idx as usize;
if i < self.chunk.constants.len() {
if let LuaConstant::String(s) = &self.chunk.constants[i] {
return String::from_utf8_lossy(s.as_ref()).into_owned();
}
}
format!("_K{}", idx)
}
pub(super) fn upvalue_expr(&self, idx: u32) -> Expr {
let i = idx as usize;
if i < self.resolved_upvalues.len() {
if let Some(expr) = &self.resolved_upvalues[i] {
return expr.clone();
}
}
if i < self.chunk.upvalue_names.len() {
let name = String::from_utf8_lossy(&self.chunk.upvalue_names[i]).into_owned();
if !name.is_empty() {
return Expr::Name(name);
}
}
Expr::Upvalue(idx)
}
}