1#[allow(unused_imports)]
11use crate::prelude::*;
12use crate::state::LuaState;
13use crate::zio::ZIO;
14use lua_types::error::LuaError;
15use lua_types::value::LuaValue;
16
17use lua_types::closure::LuaLClosure;
18use lua_types::gc::GcRef;
19use lua_types::opcode::Instruction;
20use lua_types::proto::{AbsLineInfo, LocalVar, LuaProto, UpvalDesc};
21use lua_types::string::LuaString;
22use lua_types::LuaVersion;
23
24const LUAC_DATA: &[u8] = b"\x19\x93\r\n\x1a\n";
28
29const LUAC_INT: i64 = 0x5678;
32
33const LUAC_NUM: f64 = 370.5;
35
36const LUAC_INT_55: i64 = -0x5678;
37
38const LUAC_INST_55: u32 = 0x12345678;
39
40const LUAC_NUM_55: f64 = -370.5;
41
42const LUAC_VERSION_51: u8 = 0x51;
45const LUAC_VERSION_52: u8 = 0x52;
46const LUAC_VERSION_53: u8 = 0x53;
47const LUAC_VERSION_54: u8 = 0x54;
48const LUAC_VERSION_55: u8 = 0x55;
49
50const LUAC_FORMAT: u8 = 0;
51
52const LUA_SIGNATURE: &[u8] = b"\x1bLua";
53
54const MAX_SHORT_LEN: usize = 40;
55
56const TAG_NIL: u8 = 0x00;
66const TAG_FALSE: u8 = 0x01;
67const TAG_TRUE: u8 = 0x11;
68const TAG_INT: u8 = 0x03;
69const TAG_FLOAT: u8 = 0x13;
70const TAG_SHORT_STR: u8 = 0x04;
71const TAG_LONG_STR: u8 = 0x14;
72
73struct LoadState<'a> {
80 state: &'a mut LuaState,
81 z: &'a mut ZIO,
82}
83
84fn load_error(_s: &LoadState<'_>, why: &'static str) -> LuaError {
91 LuaError::syntax(format_args!("bad binary format ({})", why))
92}
93
94fn load_block(s: &mut LoadState<'_>, buf: &mut [u8]) -> Result<(), LuaError> {
100 if s.z.read(s.state, buf)? != 0 {
101 return Err(load_error(s, "truncated chunk"));
102 }
103 Ok(())
104}
105
106fn load_byte(s: &mut LoadState<'_>) -> Result<u8, LuaError> {
108 let b = s.z.getc(s.state)?;
109 if b == crate::zio::EOZ {
110 return Err(load_error(s, "truncated chunk"));
111 }
112 Ok(b as u8)
113}
114
115fn load_unsigned(s: &mut LoadState<'_>, limit: usize) -> Result<usize, LuaError> {
122 let mut x: usize = 0;
123 let limit = limit >> 7;
124 loop {
125 let b = load_byte(s)? as usize;
126 if x >= limit {
127 return Err(load_error(s, "integer overflow"));
128 }
129 x = (x << 7) | (b & 0x7f);
130 if (b & 0x80) != 0 {
131 break;
132 }
133 }
134 Ok(x)
135}
136
137fn load_size(s: &mut LoadState<'_>) -> Result<usize, LuaError> {
139 load_unsigned(s, usize::MAX)
140}
141
142fn load_int(s: &mut LoadState<'_>) -> Result<i32, LuaError> {
144 let v = load_unsigned(s, i32::MAX as usize)?;
145 Ok(v as i32)
146}
147
148fn load_number(s: &mut LoadState<'_>) -> Result<f64, LuaError> {
154 let mut buf = [0u8; 8];
155 load_block(s, &mut buf)?;
156 Ok(f64::from_ne_bytes(buf))
157}
158
159fn load_integer(s: &mut LoadState<'_>) -> Result<i64, LuaError> {
162 let mut buf = [0u8; 8];
163 load_block(s, &mut buf)?;
164 Ok(i64::from_ne_bytes(buf))
165}
166
167fn load_raw_i32(s: &mut LoadState<'_>) -> Result<i32, LuaError> {
168 let mut buf = [0u8; 4];
169 load_block(s, &mut buf)?;
170 Ok(i32::from_ne_bytes(buf))
171}
172
173fn load_raw_u32(s: &mut LoadState<'_>) -> Result<u32, LuaError> {
174 let mut buf = [0u8; 4];
175 load_block(s, &mut buf)?;
176 Ok(u32::from_ne_bytes(buf))
177}
178
179fn load_string_n(
195 s: &mut LoadState<'_>,
196 _proto: &LuaProto,
197) -> Result<Option<GcRef<LuaString>>, LuaError> {
198 let raw_size = load_size(s)?;
199 if raw_size == 0 {
200 return Ok(None);
201 }
202 let size = raw_size - 1;
203
204 let mut buf = vec![0u8; size];
206
207 if size <= MAX_SHORT_LEN {
208 load_block(s, &mut buf)?;
209 } else {
210 load_block(s, &mut buf)?;
211 }
212
213 let ts = s.state.intern_str(&buf)?;
214
215 Ok(Some(ts))
216}
217
218fn load_string(s: &mut LoadState<'_>, proto: &LuaProto) -> Result<GcRef<LuaString>, LuaError> {
220 match load_string_n(s, proto)? {
221 Some(ts) => Ok(ts),
222 None => Err(load_error(s, "bad format for constant string")),
223 }
224}
225
226fn load_code(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
233 let n = load_int(s)? as usize;
234 let mut code = Vec::with_capacity(n);
235 for _ in 0..n {
236 let mut buf = [0u8; 4];
237 load_block(s, &mut buf)?;
238 code.push(Instruction(u32::from_ne_bytes(buf)));
239 }
240 f.code = code;
241 Ok(())
242}
243
244fn load_constants(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
248 let n = load_int(s)? as usize;
249 let mut k = Vec::with_capacity(n);
250
251 for _ in 0..n {
252 let t = load_byte(s)?;
253 let val = match t {
254 TAG_NIL => LuaValue::Nil,
255 TAG_FALSE => LuaValue::Bool(false),
256 TAG_TRUE => LuaValue::Bool(true),
257 TAG_FLOAT => LuaValue::Float(load_number(s)?),
258 TAG_INT => LuaValue::Int(load_integer(s)?),
259
260 TAG_SHORT_STR | TAG_LONG_STR => {
261 let ts = load_string(s, f)?;
262 LuaValue::Str(ts)
263 }
264
265 _ => {
266 debug_assert!(false, "unknown constant type tag {:#04x}", t);
267 LuaValue::Nil
268 }
269 };
270 k.push(val);
271 }
272
273 f.k = k;
274 Ok(())
275}
276
277fn load_protos(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
282 let n = load_int(s)? as usize;
283 let mut protos = Vec::with_capacity(n);
284
285 for _ in 0..n {
286 let mut sub = LuaProto::placeholder();
287
288 let parent_source = f.source.clone();
290 load_function(s, &mut sub, parent_source)?;
291
292 let sub_ref = GcRef::new(sub);
294 sub_ref.account_buffer(sub_ref.buffer_bytes() as isize);
295 protos.push(sub_ref);
296 }
297
298 f.p = protos;
299 Ok(())
300}
301
302fn load_upvalues(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
309 let n = load_int(s)? as usize;
310
311 let mut upvalues = Vec::with_capacity(n);
312 for _ in 0..n {
313 let instack_raw = load_byte(s)?;
314 let idx = load_byte(s)?;
315 let kind = load_byte(s)?;
316
317 upvalues.push(UpvalDesc {
318 name: None, instack: instack_raw != 0,
320 idx,
321 kind,
322 });
323 }
324
325 f.upvalues = upvalues;
326 Ok(())
327}
328
329fn load_debug(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
337 let n = load_int(s)? as usize;
338 let mut lineinfo = vec![0i8; n];
339 for item in lineinfo.iter_mut() {
340 *item = load_byte(s)? as i8;
341 }
342 f.lineinfo = lineinfo;
343
344 let n = load_int(s)? as usize;
345 let mut abslineinfo = Vec::with_capacity(n);
346 for _ in 0..n {
347 abslineinfo.push(AbsLineInfo {
348 pc: load_int(s)?,
349 line: load_int(s)?,
350 });
351 }
352 f.abslineinfo = abslineinfo;
353
354 let n = load_int(s)? as usize;
355
356 let mut locvars = Vec::with_capacity(n);
357 for _ in 0..n {
358 let varname = load_string_n(s, f)?;
359 let startpc = load_int(s)?;
360 let endpc = load_int(s)?;
361 let varname = match varname {
362 Some(v) => v,
363 None => s.state.new_string(b"")?,
364 };
365 locvars.push(LocalVar {
366 varname,
367 startpc,
368 endpc,
369 });
370 }
371 f.locvars = locvars;
372
373 let has_names = load_int(s)?;
375 if has_names != 0 {
376 let n_upvals = f.upvalues.len();
377 for i in 0..n_upvals {
378 let name = load_string_n(s, f)?;
379 f.upvalues[i].name = name;
380 }
381 }
382
383 Ok(())
384}
385
386fn load_function(
394 s: &mut LoadState<'_>,
395 f: &mut LuaProto,
396 psource: Option<GcRef<LuaString>>,
397) -> Result<(), LuaError> {
398 let source = load_string_n(s, f)?;
399 f.source = source.or(psource);
400
401 f.linedefined = load_int(s)?;
402 f.lastlinedefined = load_int(s)?;
403 f.numparams = load_byte(s)?;
404 f.is_vararg = load_byte(s)? != 0;
405 f.maxstacksize = load_byte(s)?;
406 load_code(s, f)?;
407 reconstruct_vararg_table_reg(f);
408 load_constants(s, f)?;
409 load_upvalues(s, f)?;
410 load_protos(s, f)?;
411 load_debug(s, f)?;
412
413 Ok(())
414}
415
416fn reconstruct_vararg_table_reg(f: &mut LuaProto) {
425 const OP_VARARGPACK: u32 = 84;
426 const OPCODE_MASK: u32 = 0x7F;
427 const POS_K: u32 = 15;
428 if let Some((reg, needed)) = f.code.iter().find_map(|inst| {
429 let raw = inst.raw();
430 (raw & OPCODE_MASK == OP_VARARGPACK).then(|| {
431 let reg = ((raw >> 7) & 0xFF) as u8;
432 let needed = ((raw >> POS_K) & 1) != 0;
433 (reg, needed)
434 })
435 }) {
436 f.vararg_table_reg = Some(reg);
437 f.vararg_table_needed = needed;
438 }
439}
440
441fn check_literal(
445 s: &mut LoadState<'_>,
446 expected: &[u8],
447 msg: &'static str,
448) -> Result<(), LuaError> {
449 let mut buf = vec![0u8; expected.len()];
450 load_block(s, &mut buf)?;
451 if buf != expected {
452 return Err(load_error(s, msg));
453 }
454 Ok(())
455}
456
457fn fcheck_size(
460 s: &mut LoadState<'_>,
461 expected_size: usize,
462 tname: &'static str,
463) -> Result<(), LuaError> {
464 let b = load_byte(s)? as usize;
465 if b != expected_size {
466 return Err(LuaError::syntax(format_args!("{} size mismatch", tname)));
467 }
468 Ok(())
469}
470
471fn check_header(s: &mut LoadState<'_>) -> Result<(), LuaError> {
480 check_literal(s, &LUA_SIGNATURE[1..], "not a binary chunk")?;
482
483 let version = s.state.global().lua_version;
484 let expected_version = match version {
485 LuaVersion::V51 => LUAC_VERSION_51,
486 LuaVersion::V52 => LUAC_VERSION_52,
487 LuaVersion::V53 => LUAC_VERSION_53,
488 LuaVersion::V55 => LUAC_VERSION_55,
489 _ => LUAC_VERSION_54,
490 };
491 let ver = load_byte(s)?;
492 if ver != expected_version {
493 return Err(load_error(s, "version mismatch"));
494 }
495
496 let fmt = load_byte(s)?;
497 if fmt != LUAC_FORMAT {
498 return Err(load_error(s, "format mismatch"));
499 }
500
501 match version {
502 LuaVersion::V51 => {
503 check_legacy_sizes(s)?;
504 }
505 LuaVersion::V52 => {
506 check_legacy_sizes(s)?;
507 check_literal(s, LUAC_DATA, "corrupted chunk")?;
508 }
509 LuaVersion::V53 => {
510 check_literal(s, LUAC_DATA, "corrupted chunk")?;
511 fcheck_size(s, size_of::<i32>(), "int")?;
512 fcheck_size(s, size_of::<usize>(), "size_t")?;
513 fcheck_size(s, 4, "Instruction")?;
514 fcheck_size(s, 8, "lua_Integer")?;
515 fcheck_size(s, 8, "lua_Number")?;
516 if load_integer(s)? != LUAC_INT {
517 return Err(load_error(s, "integer format mismatch"));
518 }
519 if load_number(s)? != LUAC_NUM {
520 return Err(load_error(s, "float format mismatch"));
521 }
522 }
523 LuaVersion::V55 => {
524 check_literal(s, LUAC_DATA, "corrupted chunk")?;
525 fcheck_size(s, 4, "int")?;
526 if load_raw_i32(s)? != LUAC_INT_55 as i32 {
527 return Err(load_error(s, "int format mismatch"));
528 }
529
530 fcheck_size(s, 4, "instruction")?;
531 if load_raw_u32(s)? != LUAC_INST_55 {
532 return Err(load_error(s, "instruction format mismatch"));
533 }
534
535 fcheck_size(s, 8, "Lua integer")?;
536 if load_integer(s)? != LUAC_INT_55 {
537 return Err(load_error(s, "Lua integer format mismatch"));
538 }
539
540 fcheck_size(s, 8, "Lua number")?;
541 if load_number(s)? != LUAC_NUM_55 {
542 return Err(load_error(s, "Lua number format mismatch"));
543 }
544 }
545 _ => {
546 check_literal(s, LUAC_DATA, "corrupted chunk")?;
547 fcheck_size(s, 4, "Instruction")?;
548
549 fcheck_size(s, 8, "lua_Integer")?;
550
551 fcheck_size(s, 8, "lua_Number")?;
552
553 let int_check = load_integer(s)?;
554 if int_check != LUAC_INT {
555 return Err(load_error(s, "integer format mismatch"));
556 }
557
558 let num_check = load_number(s)?;
559 if num_check != LUAC_NUM {
560 return Err(load_error(s, "float format mismatch"));
561 }
562 }
563 }
564
565 Ok(())
566}
567
568fn check_legacy_sizes(s: &mut LoadState<'_>) -> Result<(), LuaError> {
574 if load_byte(s)? != 1 {
575 return Err(load_error(s, "endianness mismatch"));
576 }
577 fcheck_size(s, size_of::<i32>(), "int")?;
578 fcheck_size(s, size_of::<usize>(), "size_t")?;
579 fcheck_size(s, 4, "Instruction")?;
580 fcheck_size(s, 8, "lua_Number")?;
581 if load_byte(s)? != 0 {
582 return Err(load_error(s, "number format mismatch"));
583 }
584 Ok(())
585}
586
587pub(crate) fn undump(
610 state: &mut LuaState,
611 z: &mut ZIO,
612 _name: &[u8],
613) -> Result<GcRef<LuaLClosure>, LuaError> {
614 let mut s = LoadState { state, z };
615
616 check_header(&mut s)?;
617
618 let nupvalues = load_byte(&mut s)?;
620 let mut cl = LuaLClosure::placeholder();
621 let mut upvals_vec = Vec::with_capacity(nupvalues as usize);
622 for _ in 0..nupvalues as usize {
623 upvals_vec.push(std::cell::Cell::new(
624 s.state.new_upval_closed(LuaValue::Nil),
625 ));
626 }
627 cl.upvals = upvals_vec.into_boxed_slice();
628
629 s.state.push(LuaValue::Nil); let mut proto = LuaProto::placeholder();
634
635 load_function(&mut s, &mut proto, None)?;
636
637 let proto_ref = GcRef::new(proto);
639 proto_ref.account_buffer(proto_ref.buffer_bytes() as isize);
640
641 debug_assert_eq!(
642 nupvalues as usize,
643 proto_ref.upvalues.len(),
644 "upvalue count mismatch between closure header and prototype"
645 );
646
647 cl.proto = proto_ref;
649
650 let cl_ref = GcRef::new(cl);
652 cl_ref.account_buffer(cl_ref.buffer_bytes() as isize);
653
654 Ok(cl_ref)
655}