use std::fmt;
use crate::parser::{
ChunkHeader, Origin, ProtoFrameInfo, ProtoLineRange, ProtoSignature, RawConstPool,
RawDebugInfo, RawProto, RawString, RawUpvalueInfo,
};
#[derive(Debug, Clone, PartialEq)]
pub struct LoweredChunk {
pub header: ChunkHeader,
pub main: LoweredProto,
pub origin: Origin,
}
#[derive(Debug, Clone, PartialEq)]
pub struct LoweredProto {
pub source: Option<RawString>,
pub line_range: ProtoLineRange,
pub signature: ProtoSignature,
pub frame: ProtoFrameInfo,
pub constants: RawConstPool,
pub upvalues: RawUpvalueInfo,
pub debug_info: RawDebugInfo,
pub children: Vec<LoweredProto>,
pub instrs: Vec<LowInstr>,
pub lowering_map: LoweringMap,
pub origin: Origin,
}
pub(crate) fn resolve_env_upvalues(
raw: &RawProto,
parent_env_upvalues: Option<&[bool]>,
) -> Vec<bool> {
let count = usize::from(raw.common.upvalues.common.count);
let descriptors = &raw.common.upvalues.common.descriptors;
let mut env_upvalues = vec![false; count];
for (index, name) in raw
.common
.debug_info
.common
.upvalue_names
.iter()
.enumerate()
{
if index >= count {
break;
}
if raw_string_value(name).is_some_and(|value| value == "_ENV") {
env_upvalues[index] = true;
}
}
if let Some(parent_env_upvalues) = parent_env_upvalues {
for (index, descriptor) in descriptors.iter().enumerate() {
if index >= count || descriptor.in_stack {
continue;
}
if parent_env_upvalues
.get(usize::from(descriptor.index))
.copied()
.unwrap_or(false)
{
env_upvalues[index] = true;
}
}
} else if !env_upvalues.iter().any(|is_env| *is_env) && !env_upvalues.is_empty() {
env_upvalues[0] = true;
}
env_upvalues
}
fn raw_string_value(raw: &RawString) -> Option<&str> {
raw.text.as_ref().map(|text| text.value.as_str())
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LoweringMap {
pub low_to_raw: Vec<Vec<RawInstrRef>>,
pub raw_to_low: Vec<Vec<InstrRef>>,
pub pc_map: Vec<Vec<u32>>,
pub line_hints: Vec<Option<u32>>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct InstrRef(pub usize);
impl InstrRef {
pub const fn index(self) -> usize {
self.0
}
}
impl fmt::Display for InstrRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "@{}", self.0)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct RawInstrRef(pub usize);
impl RawInstrRef {
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Reg(pub usize);
impl Reg {
pub const fn index(self) -> usize {
self.0
}
}
impl fmt::Display for Reg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "r{}", self.0)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct RegRange {
pub start: Reg,
pub len: usize,
}
impl RegRange {
pub const fn new(start: Reg, len: usize) -> Self {
Self { start, len }
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ConstRef(pub usize);
impl ConstRef {
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct NumberLiteral(pub u64);
impl NumberLiteral {
pub fn from_f64(value: f64) -> Self {
Self(value.to_bits())
}
pub fn to_f64(self) -> f64 {
f64::from_bits(self.0)
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct UpvalueRef(pub usize);
impl UpvalueRef {
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ProtoRef(pub usize);
impl ProtoRef {
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ValueOperand {
Reg(Reg),
Const(ConstRef),
Integer(i64),
}
#[derive(Debug, Clone, PartialEq)]
pub enum LowInstr {
Move(MoveInstr),
LoadNil(LoadNilInstr),
LoadBool(LoadBoolInstr),
LoadConst(LoadConstInstr),
LoadInteger(LoadIntegerInstr),
LoadNumber(LoadNumberInstr),
UnaryOp(UnaryOpInstr),
BinaryOp(BinaryOpInstr),
Concat(ConcatInstr),
GetUpvalue(GetUpvalueInstr),
SetUpvalue(SetUpvalueInstr),
GetTable(GetTableInstr),
SetTable(SetTableInstr),
ErrNil(ErrNilInstr),
NewTable(NewTableInstr),
SetList(SetListInstr),
Call(CallInstr),
TailCall(TailCallInstr),
VarArg(VarArgInstr),
Return(ReturnInstr),
Closure(ClosureInstr),
Close(CloseInstr),
Tbc(TbcInstr),
NumericForInit(NumericForInitInstr),
NumericForLoop(NumericForLoopInstr),
GenericForCall(GenericForCallInstr),
GenericForLoop(GenericForLoopInstr),
Jump(JumpInstr),
Branch(BranchInstr),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum UnaryOpKind {
Not,
Neg,
BitNot,
Length,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum BinaryOpKind {
Add,
Sub,
Mul,
Div,
FloorDiv,
Mod,
Pow,
BitAnd,
BitOr,
BitXor,
Shl,
Shr,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum CallKind {
Normal,
Method,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct MethodNameHint {
pub const_ref: ConstRef,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ValuePack {
Fixed(RegRange),
Open(Reg),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ResultPack {
Fixed(RegRange),
Open(Reg),
Ignore,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum AccessBase {
Reg(Reg),
Env,
Upvalue(UpvalueRef),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum AccessKey {
Reg(Reg),
Const(ConstRef),
Integer(i64),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum CaptureSource {
Reg(Reg),
Upvalue(UpvalueRef),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
pub enum DialectCaptureExtra {
#[default]
None,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct Capture {
pub source: CaptureSource,
pub extra: DialectCaptureExtra,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum BranchPredicate {
Truthy,
Eq,
Lt,
Le,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum CondOperand {
Reg(Reg),
Const(ConstRef),
Nil,
Boolean(bool),
Integer(i64),
Number(NumberLiteral),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum BranchOperands {
Unary(CondOperand),
Binary(CondOperand, CondOperand),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct BranchCond {
pub predicate: BranchPredicate,
pub operands: BranchOperands,
pub negated: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct MoveInstr {
pub dst: Reg,
pub src: Reg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct LoadNilInstr {
pub dst: RegRange,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct LoadBoolInstr {
pub dst: Reg,
pub value: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct LoadConstInstr {
pub dst: Reg,
pub value: ConstRef,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LoadIntegerInstr {
pub dst: Reg,
pub value: i64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LoadNumberInstr {
pub dst: Reg,
pub value: f64,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct UnaryOpInstr {
pub dst: Reg,
pub op: UnaryOpKind,
pub src: Reg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct BinaryOpInstr {
pub dst: Reg,
pub op: BinaryOpKind,
pub lhs: ValueOperand,
pub rhs: ValueOperand,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ConcatInstr {
pub dst: Reg,
pub src: RegRange,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct GetUpvalueInstr {
pub dst: Reg,
pub src: UpvalueRef,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct SetUpvalueInstr {
pub dst: UpvalueRef,
pub src: Reg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct GetTableInstr {
pub dst: Reg,
pub base: AccessBase,
pub key: AccessKey,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct SetTableInstr {
pub base: AccessBase,
pub key: AccessKey,
pub value: ValueOperand,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ErrNilInstr {
pub subject: Reg,
pub name: Option<ConstRef>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct NewTableInstr {
pub dst: Reg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct SetListInstr {
pub base: Reg,
pub values: ValuePack,
pub start_index: u32,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct CallInstr {
pub callee: Reg,
pub args: ValuePack,
pub results: ResultPack,
pub kind: CallKind,
pub method_name: Option<MethodNameHint>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct TailCallInstr {
pub callee: Reg,
pub args: ValuePack,
pub kind: CallKind,
pub method_name: Option<MethodNameHint>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct VarArgInstr {
pub results: ResultPack,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ReturnInstr {
pub values: ValuePack,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ClosureInstr {
pub dst: Reg,
pub proto: ProtoRef,
pub captures: Vec<Capture>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct CloseInstr {
pub from: Reg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct TbcInstr {
pub reg: Reg,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct NumericForInitInstr {
pub index: Reg,
pub limit: Reg,
pub step: Reg,
pub binding: Reg,
pub body_target: InstrRef,
pub exit_target: InstrRef,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct NumericForLoopInstr {
pub index: Reg,
pub limit: Reg,
pub step: Reg,
pub binding: Reg,
pub body_target: InstrRef,
pub exit_target: InstrRef,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct GenericForCallInstr {
pub state: RegRange,
pub results: ResultPack,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct GenericForLoopInstr {
pub control: Reg,
pub bindings: RegRange,
pub body_target: InstrRef,
pub exit_target: InstrRef,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct JumpInstr {
pub target: InstrRef,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct BranchInstr {
pub cond: BranchCond,
pub then_target: InstrRef,
pub else_target: InstrRef,
}