mod error;
mod header;
mod luna;
mod puc;
mod puc_writer;
mod reader;
mod verify;
use crate::runtime::function::Proto;
use crate::runtime::heap::{Gc, Heap};
use crate::version::LuaVersion;
pub fn dump(proto: &Proto, strip: bool, version: LuaVersion) -> Vec<u8> {
luna::dump(proto, strip, version)
}
pub(crate) fn dump_puc(proto: &Proto, strip: bool, version: LuaVersion) -> Result<Vec<u8>, String> {
puc_writer::dump(proto, strip, version)
}
pub fn is_binary_chunk(bytes: &[u8]) -> bool {
bytes.first() == Some(&0x1b)
}
pub fn undump(
bytes: &[u8],
heap: &mut Heap,
version: LuaVersion,
allow_puc: bool,
) -> Result<Gc<Proto>, String> {
undump_checked(bytes, heap, version, allow_puc).map_err(|r| match r {
Refusal::Gate(msg) => msg,
Refusal::Bad(bad) => bad.render(version),
})
}
pub(crate) fn undump_named(
bytes: &[u8],
heap: &mut Heap,
version: LuaVersion,
allow_puc: bool,
chunkname: &[u8],
) -> Result<Gc<Proto>, Vec<u8>> {
undump_checked(bytes, heap, version, allow_puc).map_err(|r| match r {
Refusal::Gate(msg) => msg.into_bytes(),
Refusal::Bad(bad) => {
let mut out = error::lundump_name(chunkname).to_vec();
out.extend_from_slice(b": ");
out.extend_from_slice(bad.render(version).as_bytes());
out
}
})
}
enum Refusal {
Gate(String),
Bad(error::Bad),
}
impl From<error::Bad> for Refusal {
fn from(bad: error::Bad) -> Refusal {
Refusal::Bad(bad)
}
}
fn undump_checked(
bytes: &[u8],
heap: &mut Heap,
version: LuaVersion,
allow_puc: bool,
) -> Result<Gc<Proto>, Refusal> {
if bytes.first() != Some(&0x1b) {
return Err(Refusal::Gate("not a binary chunk".to_string()));
}
let header = luna::header_for(version);
let tag_at = header.len();
let luna_body = bytes.len() >= tag_at + luna::BODY_TAG.len()
&& &bytes[tag_at..tag_at + luna::BODY_TAG.len()] == luna::BODY_TAG;
let written_version_byte = header[4];
let puc_signature =
bytes.len() >= 5 && &bytes[0..4] == b"\x1bLua" && matches!(bytes[4], 0x51..=0x55);
let own_puc = puc_signature && !luna_body && Some(bytes[4]) == own_puc_version(version);
let foreign_puc = puc_signature
&& !luna_body
&& !own_puc
&& (bytes[4] != written_version_byte || bytes.len() >= tag_at + luna::BODY_TAG.len());
if foreign_puc && !allow_puc {
return Err(Refusal::Gate(
"PUC bytecode loading is disabled \
(call vm.set_puc_bytecode_loading(true) to enable)"
.to_string(),
));
}
let proto = if own_puc || foreign_puc {
puc::undump_puc(bytes, heap)?
} else {
luna::undump(bytes, heap, version)?
};
verify::verify(&proto).map_err(error::Bad::Code)?;
Ok(proto)
}
fn own_puc_version(version: LuaVersion) -> Option<u8> {
match version {
LuaVersion::Lua51 => Some(0x51),
LuaVersion::Lua52 => Some(0x52),
LuaVersion::Lua53 => Some(0x53),
LuaVersion::Lua54 => Some(0x54),
LuaVersion::Lua55 => Some(0x55),
LuaVersion::MacroLua => None,
}
}