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 s.state.mark_gc_check_needed();
300 let sub_ref = GcRef::new(sub);
301 sub_ref.account_buffer(sub_ref.buffer_bytes() as isize);
302 protos.push(sub_ref);
303 }
304
305 f.p = protos;
306 Ok(())
307}
308
309fn load_upvalues(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
316 let n = load_int(s)? as usize;
317
318 let mut upvalues = Vec::with_capacity(n);
319 for _ in 0..n {
320 let instack_raw = load_byte(s)?;
321 let idx = load_byte(s)?;
322 let kind = load_byte(s)?;
323
324 upvalues.push(UpvalDesc {
325 name: None, instack: instack_raw != 0,
327 idx,
328 kind,
329 });
330 }
331
332 f.upvalues = upvalues;
333 Ok(())
334}
335
336fn load_debug(s: &mut LoadState<'_>, f: &mut LuaProto) -> Result<(), LuaError> {
344 let n = load_int(s)? as usize;
345 let mut lineinfo = vec![0i8; n];
346 for item in lineinfo.iter_mut() {
347 *item = load_byte(s)? as i8;
348 }
349 f.lineinfo = lineinfo;
350
351 let n = load_int(s)? as usize;
352 let mut abslineinfo = Vec::with_capacity(n);
353 for _ in 0..n {
354 abslineinfo.push(AbsLineInfo {
355 pc: load_int(s)?,
356 line: load_int(s)?,
357 });
358 }
359 f.abslineinfo = abslineinfo;
360
361 let n = load_int(s)? as usize;
362
363 let mut locvars = Vec::with_capacity(n);
364 for _ in 0..n {
365 let varname = load_string_n(s, f)?;
366 let startpc = load_int(s)?;
367 let endpc = load_int(s)?;
368 let varname = match varname {
369 Some(v) => v,
370 None => s.state.new_string(b"")?,
371 };
372 locvars.push(LocalVar {
373 varname,
374 startpc,
375 endpc,
376 });
377 }
378 f.locvars = locvars;
379
380 let has_names = load_int(s)?;
382 if has_names != 0 {
383 let n_upvals = f.upvalues.len();
384 for i in 0..n_upvals {
385 let name = load_string_n(s, f)?;
386 f.upvalues[i].name = name;
387 }
388 }
389
390 Ok(())
391}
392
393fn load_function(
401 s: &mut LoadState<'_>,
402 f: &mut LuaProto,
403 psource: Option<GcRef<LuaString>>,
404) -> Result<(), LuaError> {
405 let source = load_string_n(s, f)?;
406 f.source = source.or(psource);
407
408 f.linedefined = load_int(s)?;
409 f.lastlinedefined = load_int(s)?;
410 f.numparams = load_byte(s)?;
411 f.is_vararg = load_byte(s)? != 0;
412 f.maxstacksize = load_byte(s)?;
413 load_code(s, f)?;
414 reconstruct_vararg_table_reg(f);
415 load_constants(s, f)?;
416 load_upvalues(s, f)?;
417 load_protos(s, f)?;
418 load_debug(s, f)?;
419
420 Ok(())
421}
422
423fn reconstruct_vararg_table_reg(f: &mut LuaProto) {
432 const OP_VARARGPACK: u32 = 84;
433 const OPCODE_MASK: u32 = 0x7F;
434 const POS_K: u32 = 15;
435 if let Some((reg, needed)) = f.code.iter().find_map(|inst| {
436 let raw = inst.raw();
437 (raw & OPCODE_MASK == OP_VARARGPACK).then(|| {
438 let reg = ((raw >> 7) & 0xFF) as u8;
439 let needed = ((raw >> POS_K) & 1) != 0;
440 (reg, needed)
441 })
442 }) {
443 f.vararg_table_reg = Some(reg);
444 f.vararg_table_needed = needed;
445 }
446}
447
448fn check_literal(
452 s: &mut LoadState<'_>,
453 expected: &[u8],
454 msg: &'static str,
455) -> Result<(), LuaError> {
456 let mut buf = vec![0u8; expected.len()];
457 load_block(s, &mut buf)?;
458 if buf != expected {
459 return Err(load_error(s, msg));
460 }
461 Ok(())
462}
463
464fn fcheck_size(
467 s: &mut LoadState<'_>,
468 expected_size: usize,
469 tname: &'static str,
470) -> Result<(), LuaError> {
471 let b = load_byte(s)? as usize;
472 if b != expected_size {
473 return Err(LuaError::syntax(format_args!("{} size mismatch", tname)));
474 }
475 Ok(())
476}
477
478fn check_header(s: &mut LoadState<'_>) -> Result<(), LuaError> {
487 check_literal(s, &LUA_SIGNATURE[1..], "not a binary chunk")?;
489
490 let version = s.state.global().lua_version;
491 let expected_version = match version {
492 LuaVersion::V51 => LUAC_VERSION_51,
493 LuaVersion::V52 => LUAC_VERSION_52,
494 LuaVersion::V53 => LUAC_VERSION_53,
495 LuaVersion::V55 => LUAC_VERSION_55,
496 _ => LUAC_VERSION_54,
497 };
498 let ver = load_byte(s)?;
499 if ver != expected_version {
500 return Err(load_error(s, "version mismatch"));
501 }
502
503 let fmt = load_byte(s)?;
504 if fmt != LUAC_FORMAT {
505 return Err(load_error(s, "format mismatch"));
506 }
507
508 match version {
509 LuaVersion::V51 => {
510 check_legacy_sizes(s)?;
511 }
512 LuaVersion::V52 => {
513 check_legacy_sizes(s)?;
514 check_literal(s, LUAC_DATA, "corrupted chunk")?;
515 }
516 LuaVersion::V53 => {
517 check_literal(s, LUAC_DATA, "corrupted chunk")?;
518 fcheck_size(s, size_of::<i32>(), "int")?;
519 fcheck_size(s, size_of::<usize>(), "size_t")?;
520 fcheck_size(s, 4, "Instruction")?;
521 fcheck_size(s, 8, "lua_Integer")?;
522 fcheck_size(s, 8, "lua_Number")?;
523 if load_integer(s)? != LUAC_INT {
524 return Err(load_error(s, "integer format mismatch"));
525 }
526 if load_number(s)? != LUAC_NUM {
527 return Err(load_error(s, "float format mismatch"));
528 }
529 }
530 LuaVersion::V55 => {
531 check_literal(s, LUAC_DATA, "corrupted chunk")?;
532 fcheck_size(s, 4, "int")?;
533 if load_raw_i32(s)? != LUAC_INT_55 as i32 {
534 return Err(load_error(s, "int format mismatch"));
535 }
536
537 fcheck_size(s, 4, "instruction")?;
538 if load_raw_u32(s)? != LUAC_INST_55 {
539 return Err(load_error(s, "instruction format mismatch"));
540 }
541
542 fcheck_size(s, 8, "Lua integer")?;
543 if load_integer(s)? != LUAC_INT_55 {
544 return Err(load_error(s, "Lua integer format mismatch"));
545 }
546
547 fcheck_size(s, 8, "Lua number")?;
548 if load_number(s)? != LUAC_NUM_55 {
549 return Err(load_error(s, "Lua number format mismatch"));
550 }
551 }
552 _ => {
553 check_literal(s, LUAC_DATA, "corrupted chunk")?;
554 fcheck_size(s, 4, "Instruction")?;
555
556 fcheck_size(s, 8, "lua_Integer")?;
557
558 fcheck_size(s, 8, "lua_Number")?;
559
560 let int_check = load_integer(s)?;
561 if int_check != LUAC_INT {
562 return Err(load_error(s, "integer format mismatch"));
563 }
564
565 let num_check = load_number(s)?;
566 if num_check != LUAC_NUM {
567 return Err(load_error(s, "float format mismatch"));
568 }
569 }
570 }
571
572 Ok(())
573}
574
575fn check_legacy_sizes(s: &mut LoadState<'_>) -> Result<(), LuaError> {
581 if load_byte(s)? != 1 {
582 return Err(load_error(s, "endianness mismatch"));
583 }
584 fcheck_size(s, size_of::<i32>(), "int")?;
585 fcheck_size(s, size_of::<usize>(), "size_t")?;
586 fcheck_size(s, 4, "Instruction")?;
587 fcheck_size(s, 8, "lua_Number")?;
588 if load_byte(s)? != 0 {
589 return Err(load_error(s, "number format mismatch"));
590 }
591 Ok(())
592}
593
594pub(crate) fn undump(
619 state: &mut LuaState,
620 z: &mut ZIO,
621 _name: &[u8],
622) -> Result<GcRef<LuaLClosure>, LuaError> {
623 let mut s = LoadState { state, z };
624
625 check_header(&mut s)?;
626
627 let nupvalues = load_byte(&mut s)? as usize;
629
630 let mut proto = LuaProto::placeholder();
631
632 load_function(&mut s, &mut proto, None)?;
633
634 s.state.mark_gc_check_needed();
637 let proto_ref = GcRef::new(proto);
638 proto_ref.account_buffer(proto_ref.buffer_bytes() as isize);
639
640 debug_assert_eq!(
641 nupvalues,
642 proto_ref.upvalues.len(),
643 "upvalue count mismatch between closure header and prototype"
644 );
645
646 let cl_ref = s.state.new_lclosure(proto_ref, nupvalues);
653
654 Ok(cl_ref)
655}