1use crate::state_stub::{LuaDebug, LuaState, LuaStateStubExt as _};
20use lua_types::{
21 error::LuaError, gc::GcRef, string::LuaString, userdata::LuaUserData, value::LuaValue,
22 LuaStatus, LuaType,
23};
24
25const LEVELS1: i32 = 10;
29
30const LEVELS2: i32 = 11;
32
33const FREELIST_REF: i64 = 3; pub const LUA_REFNIL: i32 = -1;
39
40pub const LUA_NOREF: i32 = -2;
42
43pub const LUA_ERRFILE: i32 = 6;
45
46pub const LUA_LOADED_TABLE: &[u8] = b"_LOADED";
48
49pub const LUA_PRELOAD_TABLE: &[u8] = b"_PRELOAD";
51
52pub const LUA_GNAME: &[u8] = b"_G";
54
55pub const LUA_FILE_HANDLE: &[u8] = b"FILE*";
57
58const LUA_REGISTRYINDEX: i32 = -1_001_000;
60
61#[expect(
63 dead_code,
64 reason = "ported stdlib helper; not yet wired into the runtime"
65)]
66const LUA_MINSTACK: i32 = 20;
67
68pub struct LuaReg {
76 pub name: &'static [u8],
77 pub func: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
78}
79
80pub struct LuaBuffer {
87 pub data: Vec<u8>,
88}
89
90pub struct LuaStream {
95 pub f: Option<Box<dyn std::io::Read>>,
97 pub closef: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
99}
100
101fn find_field(state: &mut LuaState, objidx: i32, level: i32) -> Result<bool, LuaError> {
108 if level == 0 || state.type_at(-1) != LuaType::Table {
109 return Ok(false);
110 }
111 state.push(LuaValue::Nil);
112 while state.table_next(-2)? {
113 if state.type_at(-2) == LuaType::String {
114 if state.raw_equal(objidx, -1)? {
115 state.pop_n(1); return Ok(true);
117 } else if find_field(state, objidx, level - 1)? {
118 state.push_string(b".")?; state.replace(-3)?; state.concat(3)?; return Ok(true);
123 }
124 }
125 state.pop_n(1); }
127 Ok(false)
128}
129
130fn push_global_func_name(state: &mut LuaState, ar: &mut LuaDebug) -> Result<bool, LuaError> {
134 if state.global().lua_version == lua_types::LuaVersion::V51 {
135 return Ok(false);
136 }
137 let top = state.top_count();
138 state.get_info(b"f", ar)?;
139 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
140 check_stack(state, 6, Some(b"not enough stack"))?;
141 if find_field(state, top + 1, 2)? {
142 if state
143 .peek_bytes(-1)
144 .map_or(false, |n| n.starts_with(b"_G."))
145 {
146 let suffix = state
147 .peek_bytes(-1)
148 .map(|n| n[3..].to_vec())
149 .unwrap_or_default();
150 state.push_bytes(&suffix)?;
151 state.remove(-2)?;
152 }
153 state.copy_value(-1, top + 1)?;
154 lua_vm::api::set_top(state, top + 1)?;
155 Ok(true)
156 } else {
157 lua_vm::api::set_top(state, top)?;
158 Ok(false)
159 }
160}
161
162fn push_global_func_name_from_target(
163 state: &mut LuaState,
164 target: &mut LuaState,
165 ar: &mut LuaDebug,
166) -> Result<bool, LuaError> {
167 if state.global().lua_version == lua_types::LuaVersion::V51 {
168 return Ok(false);
169 }
170 let top = state.top_count();
171 target.get_info(b"f", ar)?;
172 let func = target.get_at(target.top_idx() - 1);
173 target.pop_n(1);
174 state.push(func);
175 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
176 check_stack(state, 6, Some(b"not enough stack"))?;
177 if find_field(state, top + 1, 2)? {
178 if state
179 .peek_bytes(-1)
180 .map_or(false, |n| n.starts_with(b"_G."))
181 {
182 let suffix = state
183 .peek_bytes(-1)
184 .map(|n| n[3..].to_vec())
185 .unwrap_or_default();
186 state.push_bytes(&suffix)?;
187 state.remove(-2)?;
188 }
189 state.copy_value(-1, top + 1)?;
190 lua_vm::api::set_top(state, top + 1)?;
191 Ok(true)
192 } else {
193 lua_vm::api::set_top(state, top)?;
194 Ok(false)
195 }
196}
197
198fn push_func_name(
207 state: &mut LuaState,
208 ar: &mut LuaDebug,
209 global_lookup_target: Option<&mut LuaState>,
210) -> Result<(), LuaError> {
211 if state.global().lua_version == lua_types::LuaVersion::V52 {
212 if !ar.namewhat.is_empty() {
213 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
214 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
215 } else if ar.what == b'm' {
216 state.push_string(b"main chunk")?;
217 } else if ar.what == b'C' {
218 state.push_string(b"?")?;
219 } else {
220 let src = ar.short_src.clone();
221 let line = ar.linedefined;
222 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
223 }
224 return Ok(());
225 }
226 let namewhat_first = state.global().lua_version == lua_types::LuaVersion::V55;
231 if namewhat_first && !ar.namewhat.is_empty() {
232 let namewhat = ar.namewhat.clone();
233 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
234 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
235 return Ok(());
236 }
237 let found_global = match global_lookup_target {
238 Some(target) => push_global_func_name_from_target(state, target, ar)?,
239 None => push_global_func_name(state, ar)?,
240 };
241 if found_global {
242 let name = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
243 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
244 state.remove(-2)?;
245 } else if !ar.namewhat.is_empty() {
246 let namewhat = ar.namewhat.clone();
247 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
248 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
249 } else if ar.what == b'm' {
250 state.push_string(b"main chunk")?;
251 } else if ar.what != b'C' {
252 let src = ar.short_src.clone();
253 let line = ar.linedefined;
254 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
255 } else {
256 state.push_string(b"?")?;
257 }
258 Ok(())
259}
260
261fn last_level(state: &mut LuaState) -> i32 {
264 let mut ar = LuaDebug::default();
265 let mut li: i32 = 1;
266 let mut le: i32 = 1;
267 while state.get_stack(le, &mut ar) {
268 li = le;
269 le *= 2;
270 }
271 while li < le {
273 let m = (li + le) / 2;
274 if state.get_stack(m, &mut ar) {
275 li = m + 1;
276 } else {
277 le = m;
278 }
279 }
280 le - 1
281}
282
283pub fn traceback(
293 state: &mut LuaState,
294 mut other: Option<&mut LuaState>,
295 msg: Option<&[u8]>,
296 level: i32,
297) -> Result<(), LuaError> {
298 if state.global().lua_version == lua_types::LuaVersion::V51 {
299 return traceback_51(state, other, msg, level);
300 }
301
302 let mut b = LuaBuffer::new();
303 let mut ar = LuaDebug::default();
304 let last = match &mut other {
305 Some(o) => last_level(o),
306 None => last_level(state),
307 };
308 let mut limit2show: i32 = if last - level > LEVELS1 + LEVELS2 {
309 LEVELS1
310 } else {
311 -1
312 };
313 buf_init(state, &mut b);
314 if let Some(m) = msg {
315 add_lstring(&mut b, m);
316 add_char(&mut b, b'\n');
317 }
318 add_lstring(&mut b, b"stack traceback:");
319 let mut level = level;
320 loop {
321 let got = match &mut other {
322 Some(o) => o.get_stack(level, &mut ar),
323 None => state.get_stack(level, &mut ar),
324 };
325 if !got {
326 break;
327 }
328 level += 1;
329 if limit2show == 0 {
330 let n = last - level - LEVELS2 + 1;
331 state.push_fstring(format_args!("\n\t...\t(skipping {} levels)", n))?;
332 add_value(state, &mut b)?;
333 level += n;
334 limit2show = LEVELS2;
335 } else {
336 limit2show -= 1;
337 match &mut other {
338 Some(o) => o.get_info(b"Slnt", &mut ar)?,
339 None => state.get_info(b"Slnt", &mut ar)?,
340 }
341 if ar.currentline <= 0 {
342 let src = ar.short_src.clone();
343 state.push_fstring(format_args!("\n\t{}: in ", BStr(&src)))?;
344 } else {
345 let src = ar.short_src.clone();
346 let line = ar.currentline;
347 state.push_fstring(format_args!("\n\t{}:{}: in ", BStr(&src), line))?;
348 }
349 add_value(state, &mut b)?;
350 match &mut other {
351 Some(o) => push_func_name(state, &mut ar, Some(&mut **o))?,
352 None => push_func_name(state, &mut ar, None)?,
353 }
354 add_value(state, &mut b)?;
355 if ar.istailcall {
356 add_lstring(&mut b, b"\n\t(...tail calls...)");
357 }
358 }
359 }
360 push_result(state, &mut b)?;
361 Ok(())
362}
363
364fn traceback_51(
365 state: &mut LuaState,
366 mut other: Option<&mut LuaState>,
367 msg: Option<&[u8]>,
368 level: i32,
369) -> Result<(), LuaError> {
370 const LEVELS1_51: i32 = 12;
371 const LEVELS2_51: i32 = 10;
372
373 let mut b = LuaBuffer::new();
374 let mut ar = LuaDebug::default();
375 let mut firstpart = true;
376 buf_init(state, &mut b);
377 if let Some(m) = msg {
378 add_lstring(&mut b, m);
379 add_char(&mut b, b'\n');
380 }
381 add_lstring(&mut b, b"stack traceback:");
382
383 let mut level = level;
384 loop {
385 let got = match &mut other {
386 Some(o) => o.get_stack(level, &mut ar),
387 None => state.get_stack(level, &mut ar),
388 };
389 if !got {
390 break;
391 }
392 level += 1;
393 if level > LEVELS1_51 && firstpart {
394 let has_tail = match &mut other {
395 Some(o) => o.get_stack(level + LEVELS2_51, &mut ar),
396 None => state.get_stack(level + LEVELS2_51, &mut ar),
397 };
398 if !has_tail {
399 level -= 1;
400 } else {
401 add_lstring(&mut b, b"\n\t...");
402 while match &mut other {
403 Some(o) => o.get_stack(level + LEVELS2_51, &mut ar),
404 None => state.get_stack(level + LEVELS2_51, &mut ar),
405 } {
406 level += 1;
407 }
408 }
409 firstpart = false;
410 continue;
411 }
412
413 match &mut other {
414 Some(o) => o.get_info(b"Snl", &mut ar)?,
415 None => state.get_info(b"Snl", &mut ar)?,
416 }
417 add_lstring(&mut b, b"\n\t");
418 add_lstring(&mut b, &ar.short_src);
419 add_char(&mut b, b':');
420 if ar.currentline > 0 {
421 state.push_fstring(format_args!("{}:", ar.currentline))?;
422 add_value(state, &mut b)?;
423 }
424 if !ar.namewhat.is_empty() {
425 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
426 state.push_fstring(format_args!(" in function '{}'", BStr(&name)))?;
427 add_value(state, &mut b)?;
428 } else if ar.what == b'm' {
429 add_lstring(&mut b, b" in main chunk");
430 } else if ar.what == b'C' || ar.what == b't' {
431 add_lstring(&mut b, b" ?");
432 } else {
433 let src = ar.short_src.clone();
434 let line = ar.linedefined;
435 state.push_fstring(format_args!(" in function <{}:{}>", BStr(&src), line))?;
436 add_value(state, &mut b)?;
437 }
438 }
439
440 push_result(state, &mut b)?;
441 Ok(())
442}
443
444pub fn arg_error(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> Result<usize, LuaError> {
451 let mut ar = LuaDebug::default();
452 if !state.get_stack(0, &mut ar) {
453 return Err(LuaError::runtime(format_args!(
454 "bad argument #{} ({})",
455 arg,
456 BStr(extramsg)
457 )));
458 }
459 state.get_info(b"n", &mut ar)?;
460 if ar.namewhat == b"method" {
461 arg -= 1; if arg == 0 {
463 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
464 return Err(LuaError::runtime(format_args!(
465 "calling '{}' on bad self ({})",
466 BStr(&name),
467 BStr(extramsg)
468 )));
469 }
470 }
471 let fname = if ar.name.is_none() {
472 if push_global_func_name(state, &mut ar)? {
473 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
474 } else {
475 b"?".to_vec()
476 }
477 } else {
478 ar.name.clone().unwrap_or_else(|| b"?".to_vec())
479 };
480 Err(LuaError::runtime(format_args!(
481 "bad argument #{} to '{}' ({})",
482 arg,
483 BStr(&fname),
484 BStr(extramsg)
485 )))
486}
487
488pub fn type_error_arg(state: &mut LuaState, arg: i32, tname: &[u8]) -> Result<usize, LuaError> {
492 let honors_name = state.global().lua_version.honors_name_metafield();
498 let typearg: Vec<u8> = if honors_name && get_metafield(state, arg, b"__name")? == LuaType::String {
499 let bytes = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
500 state.pop_n(1);
501 bytes
502 } else if state.type_at(arg) == LuaType::LightUserData {
503 b"light userdata".to_vec()
504 } else if state.type_at(arg) == LuaType::None {
505 b"no value".to_vec()
506 } else {
507 state.type_name_at(arg).to_vec()
508 };
509 let msg_owned = format!("{} expected, got {}", BStr(tname), BStr(&typearg));
510 arg_error(state, arg, msg_owned.as_bytes())
511}
512
513fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
516 let name = state.type_name(tag);
517 type_error_arg(state, arg, name)?;
518 Ok(())
519}
520
521pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
525 let mut ar = LuaDebug::default();
526 if state.get_stack(level, &mut ar) {
527 state.get_info(b"Sl", &mut ar)?;
528 if ar.currentline > 0 {
529 let src = ar.short_src.clone();
530 let line = ar.currentline;
531 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
532 return Ok(());
533 }
534 }
535 state.push_string(b"")?;
536 Ok(())
537}
538
539pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
545 push_where(state, 1)?;
546 let where_str = state.pop_bytes();
547 let full = [where_str.as_slice(), msg].concat();
548 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
549}
550
551pub fn file_result(
556 state: &mut LuaState,
557 stat: bool,
558 fname: Option<&[u8]>,
559) -> Result<usize, LuaError> {
560 if stat {
561 state.push(LuaValue::Bool(true));
562 Ok(1)
563 } else {
564 state.push(LuaValue::Nil);
565 let errmsg = b"(errno unavailable in Rust port)".to_vec();
566 if let Some(name) = fname {
567 let full = [name, b": ".as_slice(), &errmsg].concat();
568 state.push_bytes(&full)?;
569 } else {
570 state.push_bytes(&errmsg)?;
571 }
572 state.push(LuaValue::Int(0));
573 Ok(3)
574 }
575}
576
577pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
580 if stat != 0 {
581 return file_result(state, false, None);
582 }
583 let what = b"exit".as_slice();
584 state.push(LuaValue::Bool(true));
585 state.push_bytes(what)?;
586 state.push(LuaValue::Int(stat as i64));
587 Ok(3)
588}
589
590pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
597 if get_metatable(state, tname)? != LuaType::Nil {
598 return Ok(false); }
600 state.pop_n(1);
601 state.create_table(0, 2)?;
602 state.push_bytes(tname)?;
603 state.set_field(-2, b"__name")?;
604 state.push_value(-1)?;
605 state.set_field(LUA_REGISTRYINDEX, tname)?;
606 Ok(true)
607}
608
609pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
612 get_metatable(state, tname)?;
613 state.set_metatable(-2)?;
614 Ok(())
615}
616
617pub fn test_udata(
621 state: &mut LuaState,
622 ud: i32,
623 tname: &[u8],
624) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
625 let p = state.to_userdata(ud);
626 if let Some(p) = p {
627 if state.get_metatable(ud)? {
628 get_metatable(state, tname)?;
629 let eq = state.raw_equal(-1, -2)?;
630 state.pop_n(2); if eq {
632 return Ok(Some(p));
633 }
634 }
635 }
636 Ok(None)
637}
638
639pub fn check_udata(
642 state: &mut LuaState,
643 ud: i32,
644 tname: &[u8],
645) -> Result<GcRef<LuaUserData>, LuaError> {
646 match test_udata(state, ud, tname)? {
647 Some(p) => Ok(p),
648 None => {
649 type_error_arg(state, ud, tname)?;
650 unreachable!()
651 }
652 }
653}
654
655pub fn check_option(
661 state: &mut LuaState,
662 arg: i32,
663 def: Option<&[u8]>,
664 lst: &[&[u8]],
665) -> Result<usize, LuaError> {
666 let name: Vec<u8> = match def {
667 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
668 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
669 };
670 for (i, entry) in lst.iter().enumerate() {
671 if *entry == name.as_slice() {
672 return Ok(i);
673 }
674 }
675 Err(LuaError::runtime(format_args!(
676 "invalid option '{}'",
677 BStr(&name)
678 )))
679}
680
681pub fn check_stack(state: &mut LuaState, space: i32, msg: Option<&[u8]>) -> Result<(), LuaError> {
684 if !state.check_stack_space(space) {
685 match msg {
686 Some(m) => {
687 return Err(LuaError::runtime(format_args!(
688 "stack overflow ({})",
689 BStr(m)
690 )));
691 }
692 None => {
693 return Err(LuaError::runtime(format_args!("stack overflow")));
694 }
695 }
696 }
697 Ok(())
698}
699
700pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
703 if state.type_at(arg) != t {
704 tag_error(state, arg, t)?;
705 }
706 Ok(())
707}
708
709pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
712 if state.type_at(arg) == LuaType::None {
713 arg_error(state, arg, b"value expected")?;
714 }
715 Ok(())
716}
717
718pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
721 match state.to_lua_string(arg) {
722 Some(s) => Ok(s),
723 None => {
724 tag_error(state, arg, LuaType::String)?;
725 unreachable!()
726 }
727 }
728}
729
730pub fn opt_lstring(
733 state: &mut LuaState,
734 arg: i32,
735 def: Option<&[u8]>,
736) -> Result<Option<Vec<u8>>, LuaError> {
737 if state.is_none_or_nil(arg) {
738 return Ok(def.map(|d| d.to_vec()));
739 }
740 let s = check_lstring(state, arg)?;
741 Ok(Some(s.as_bytes().to_vec()))
742}
743
744pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
747 match state.to_number_x(arg) {
748 Some(d) => Ok(d),
749 None => {
750 tag_error(state, arg, LuaType::Number)?;
751 unreachable!()
752 }
753 }
754}
755
756pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
759 if state.is_none_or_nil(arg) {
760 Ok(def)
761 } else {
762 check_number(state, arg)
763 }
764}
765
766fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
772 if state.is_number(arg) {
773 arg_error(state, arg, b"number has no integer representation")
774 } else {
775 tag_error(state, arg, LuaType::Number)?;
776 unreachable!("tag_error always returns Err")
777 }
778}
779
780pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
783 match state.to_integer_x(arg) {
784 Some(d) => Ok(d),
785 None => {
786 int_error(state, arg)?;
787 unreachable!("int_error always returns Err")
788 }
789 }
790}
791
792pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
795 if state.is_none_or_nil(arg) {
796 Ok(def)
797 } else {
798 check_integer(state, arg)
799 }
800}
801
802impl LuaBuffer {
805 pub fn new() -> Self {
809 LuaBuffer { data: Vec::new() }
810 }
811
812 pub fn len(&self) -> usize {
814 self.data.len()
815 }
816}
817
818impl Default for LuaBuffer {
819 fn default() -> Self {
820 LuaBuffer::new()
821 }
822}
823
824pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
830 *buf = LuaBuffer::new();
831 let _ = state.push(LuaValue::Nil);
832}
833
834pub fn buf_init_size(state: &mut LuaState, buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
837 buf_init(state, buf);
838 buf.data.reserve(sz);
839 Ok(())
840}
841
842fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
846 if usize::MAX - sz < buf.len() {
847 return Err(LuaError::runtime(format_args!("buffer too large")));
848 }
849 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
851 Ok(buf.len() + sz)
852 } else {
853 Ok(newsize)
854 }
855}
856
857pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
860 if buf.data.capacity() - buf.data.len() < sz {
861 let newcap = new_buff_size(buf, sz)?;
862 buf.data.reserve(newcap - buf.data.len());
863 }
864 Ok(())
865}
866
867pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
870 if !s.is_empty() {
871 buf.data.extend_from_slice(s);
872 }
873}
874
875pub fn add_char(buf: &mut LuaBuffer, c: u8) {
878 buf.data.push(c);
879}
880
881pub fn add_size(_buf: &mut LuaBuffer, sz: usize) {
887 let _ = sz;
888}
889
890pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
893 if let Some(bytes) = state.peek_bytes(-1) {
894 let owned = bytes.to_vec();
895 add_lstring(buf, &owned);
896 }
897 state.pop_n(1);
898 Ok(())
899}
900
901pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
904 state.push_bytes(&buf.data)?;
905 state.remove(-2)?;
906 Ok(())
907}
908
909pub fn push_result_size(
912 state: &mut LuaState,
913 buf: &mut LuaBuffer,
914 sz: usize,
915) -> Result<(), LuaError> {
916 add_size(buf, sz);
917 push_result(state, buf)
918}
919
920pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
924 if pat.is_empty() {
925 add_lstring(buf, s);
926 return;
927 }
928 let mut remaining = s;
929 while let Some(pos) = find_bytes(remaining, pat) {
930 add_lstring(buf, &remaining[..pos]);
931 add_lstring(buf, repl);
932 remaining = &remaining[pos + pat.len()..];
933 }
934 add_lstring(buf, remaining);
935}
936
937pub fn gsub<'a>(
941 state: &'a mut LuaState,
942 s: &[u8],
943 pat: &[u8],
944 repl: &[u8],
945) -> Result<Vec<u8>, LuaError> {
946 let mut b = LuaBuffer::new();
947 buf_init(state, &mut b);
948 add_gsub(&mut b, s, pat, repl);
949 push_result(state, &mut b)?;
950 Ok(state.peek_bytes(-1).unwrap_or_default())
951}
952
953fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
957 if needle.is_empty() {
958 return Some(0);
959 }
960 haystack.windows(needle.len()).position(|w| w == needle)
961}
962
963pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
970 if state.type_at(-1) == LuaType::Nil {
971 state.pop_n(1);
972 return Ok(LUA_REFNIL);
973 }
974 let t = state.abs_index(t);
975 let ref_val: i32;
976 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
977 ref_val = 0; state.push(LuaValue::Int(0));
979 state.raw_set_i(t, FREELIST_REF)?;
980 } else {
981 debug_assert!(state.type_at(-1) == LuaType::Number);
982 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
983 }
984 state.pop_n(1); let next_ref: i32;
986 if ref_val != 0 {
987 state.raw_get_i(t, ref_val as i64)?;
988 state.raw_set_i(t, FREELIST_REF)?;
989 next_ref = ref_val;
990 } else {
991 next_ref = (state.raw_len(t) as i32) + 1;
992 }
993 state.raw_set_i(t, next_ref as i64)?;
994 Ok(next_ref)
995}
996
997pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
1000 if r >= 0 {
1001 let t = state.abs_index(t);
1002 state.raw_get_i(t, FREELIST_REF)?;
1003 debug_assert!(state.type_at(-1) == LuaType::Number);
1004 state.raw_set_i(t, r as i64)?;
1005 state.push(LuaValue::Int(r as i64));
1006 state.raw_set_i(t, FREELIST_REF)?;
1007 }
1008 Ok(())
1009}
1010
1011fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
1016 let mut remaining = Some(data);
1017 move || remaining.take()
1018}
1019
1020fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
1028 let s = if buf.starts_with(b"\xEF\xBB\xBF") {
1029 &buf[3..]
1030 } else {
1031 buf
1032 };
1033 if s.first() == Some(&b'#') {
1034 let nl = s
1035 .iter()
1036 .position(|&b| b == b'\n')
1037 .map(|p| p + 1)
1038 .unwrap_or(s.len());
1039 let rest = &s[nl..];
1040 if rest.first() == Some(&0x1B) {
1041 rest.to_vec()
1042 } else {
1043 let mut out = Vec::with_capacity(rest.len() + 1);
1044 out.push(b'\n');
1045 out.extend_from_slice(rest);
1046 out
1047 }
1048 } else {
1049 s.to_vec()
1050 }
1051}
1052
1053pub fn load_filex(
1060 state: &mut LuaState,
1061 filename: Option<&[u8]>,
1062 mode: Option<&[u8]>,
1063) -> Result<i32, LuaError> {
1064 let _ = mode;
1065 let fname = match filename {
1066 Some(f) => f,
1067 None => {
1068 state.push_string(b"cannot read stdin: no filename given")?;
1069 return Ok(LUA_ERRFILE);
1070 }
1071 };
1072 let raw = match state.global().file_loader_hook {
1073 Some(load_fn) => load_fn(fname),
1074 None => Err(LuaError::runtime(format_args!(
1075 "no file_loader_hook registered"
1076 ))),
1077 };
1078 let raw = match raw {
1079 Ok(bytes) => bytes,
1080 Err(e) => {
1081 let detail = match e.message_bytes() {
1082 Some(b) => String::from_utf8_lossy(b).into_owned(),
1083 None => format!("{:?}", &e),
1084 };
1085 state.push_fstring(format_args!("cannot open {}: {}", BStr(fname), detail))?;
1086 return Ok(LUA_ERRFILE);
1087 }
1088 };
1089 let payload = skip_bom_and_shebang(&raw);
1090 let mut once = Some(payload);
1091 let boxed: lua_vm::zio::ChunkReader = Box::new(move |_state| Ok(once.take()));
1092 let mut chunkname = b"@".to_vec();
1093 chunkname.extend_from_slice(fname);
1094 let status = lua_vm::api::load(state, boxed, Some(&chunkname), mode)?;
1095 Ok(if status == LuaStatus::Ok {
1096 0
1097 } else {
1098 status as i32
1099 })
1100}
1101
1102pub fn load_bufferx(
1105 state: &mut LuaState,
1106 buff: &[u8],
1107 name: &[u8],
1108 mode: Option<&[u8]>,
1109) -> Result<i32, LuaError> {
1110 let _reader = make_string_reader(buff.to_vec());
1111 let ok = state.load(buff, name, mode)?;
1112 Ok(if ok { 0 } else { 1 })
1113}
1114
1115pub fn load_buffer(state: &mut LuaState, buff: &[u8], name: &[u8]) -> Result<i32, LuaError> {
1118 load_bufferx(state, buff, name, None)
1119}
1120
1121pub fn load_string(state: &mut LuaState, s: &[u8]) -> Result<i32, LuaError> {
1124 load_buffer(state, s, s)
1125}
1126
1127pub fn get_metafield(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<LuaType, LuaError> {
1133 if !state.get_metatable(obj)? {
1134 return Ok(LuaType::Nil);
1135 }
1136 state.push_bytes(event)?;
1137 let tt = state.raw_get(-2)?;
1138 if tt == LuaType::Nil {
1139 state.pop_n(2);
1140 } else {
1141 state.remove(-2)?;
1142 }
1143 Ok(tt)
1144}
1145
1146pub fn call_meta(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<bool, LuaError> {
1150 let obj = state.abs_index(obj);
1151 if get_metafield(state, obj, event)? == LuaType::Nil {
1152 return Ok(false);
1153 }
1154 state.push_value(obj)?;
1155 state.call(1, 1)?;
1156 Ok(true)
1157}
1158
1159pub fn lua_len(state: &mut LuaState, idx: i32) -> Result<i64, LuaError> {
1163 state.len_op(idx)?;
1164 let l = match state.to_integer_x(-1) {
1165 Some(n) => n,
1166 None => {
1167 return Err(LuaError::runtime(format_args!(
1168 "object length is not an integer"
1169 )));
1170 }
1171 };
1172 state.pop_n(1);
1173 Ok(l)
1174}
1175
1176pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Vec<u8>, LuaError> {
1183 let idx = state.abs_index(idx);
1184 if call_meta(state, idx, b"__tostring")? {
1185 if state.type_at(-1) != LuaType::String {
1186 return Err(LuaError::runtime(format_args!(
1187 "'__tostring' must return a string"
1188 )));
1189 }
1190 } else {
1191 match state.type_at(idx) {
1192 LuaType::Number => {
1193 if state.is_integer(idx) {
1194 let i = state.to_integer_x(idx).unwrap_or(0);
1195 state.push_fstring(format_args!("{}", i))?;
1196 } else {
1197 let f = state.to_number_x(idx).unwrap_or(0.0);
1198 state.push_fstring(format_args!("{:?}", f))?;
1199 }
1200 }
1201 LuaType::String => {
1202 state.push_value(idx)?;
1203 }
1204 LuaType::Boolean => {
1205 let b = state.to_boolean(idx);
1206 state.push_string(if b { b"true" } else { b"false" })?;
1207 }
1208 LuaType::Nil => {
1209 state.push_string(b"nil")?;
1210 }
1211 _ => {
1212 let tt = if state.global().lua_version.honors_name_metafield() {
1213 get_metafield(state, idx, b"__name")?
1214 } else {
1215 LuaType::Nil
1216 };
1217 let kind: Vec<u8> = if tt == LuaType::String {
1218 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1219 } else {
1220 state.type_name_at(idx).to_vec()
1221 };
1222 state.push_fstring(format_args!("{}: 0x?", BStr(&kind)))?;
1223 if tt != LuaType::Nil {
1224 state.remove(-2)?;
1225 }
1226 }
1227 }
1228 }
1229 Ok(state.peek_bytes(-1).unwrap_or_default())
1230}
1231
1232pub fn set_funcs(state: &mut LuaState, l: &[LuaReg], nup: i32) -> Result<(), LuaError> {
1236 check_stack(state, nup, Some(b"too many upvalues"))?;
1237 for reg in l {
1238 match reg.func {
1239 None => {
1240 state.push(LuaValue::Bool(false));
1241 }
1242 Some(f) => {
1243 for _ in 0..nup {
1244 state.push_value(-nup)?;
1245 }
1246 state.push_c_closure(f, nup)?;
1247 }
1248 }
1249 state.set_field(-(nup + 2), reg.name)?;
1250 }
1251 state.pop_n(nup as usize);
1252 Ok(())
1253}
1254
1255pub fn get_subtable(state: &mut LuaState, idx: i32, fname: &[u8]) -> Result<bool, LuaError> {
1259 if state.get_field(idx, fname)? == LuaType::Table {
1260 return Ok(true);
1261 }
1262 state.pop_n(1);
1263 let idx = state.abs_index(idx);
1264 let new_tbl = state.new_table();
1265 state.push(LuaValue::Table(new_tbl));
1266 state.push_value(-1)?;
1267 state.set_field(idx, fname)?;
1268 Ok(false)
1269}
1270
1271pub fn requiref(
1276 state: &mut LuaState,
1277 modname: &[u8],
1278 openf: fn(&mut LuaState) -> Result<usize, LuaError>,
1279 glb: bool,
1280) -> Result<(), LuaError> {
1281 get_subtable(state, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
1282 state.get_field(-1, modname)?;
1283 if !state.to_boolean(-1) {
1284 state.pop_n(1);
1285 state.push_c_function(openf)?;
1286 state.push_bytes(modname)?;
1287 state.call(1, 1)?;
1288 state.push_value(-1)?;
1289 state.set_field(-3, modname)?;
1290 }
1291 state.remove(-2)?;
1292 if glb {
1293 state.push_value(-1)?;
1294 state.set_global(modname)?;
1295 }
1296 Ok(())
1297}
1298
1299pub fn get_metatable(state: &mut LuaState, tname: &[u8]) -> Result<LuaType, LuaError> {
1304 state.get_field(LUA_REGISTRYINDEX, tname)
1305}
1306
1307pub fn new_state() -> Result<LuaState, LuaError> {
1314 let _ = default_panic_handler;
1315 let _ = warn_off;
1316 todo!("phase-b: LuaState::new()")
1317}
1318
1319fn default_panic_handler(state: &mut LuaState) -> Result<usize, LuaError> {
1322 let msg = if state.type_at(-1) == LuaType::String {
1323 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1324 } else {
1325 b"error object is not a string".to_vec()
1326 };
1327 eprintln!(
1328 "PANIC: unprotected error in call to Lua API ({})",
1329 BStr(&msg)
1330 );
1331 Ok(0) }
1333
1334fn warn_off(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1337 check_control(state, message, tocont)?;
1338 Ok(())
1339}
1340
1341fn warn_on(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1344 if check_control(state, message, tocont)? {
1345 return Ok(());
1346 }
1347 eprint!("Lua warning: ");
1348 warn_cont(state, message, tocont)
1349}
1350
1351fn warn_cont(_state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1354 eprint!("{}", BStr(message));
1355 if tocont {
1356 let _ = (warn_cont as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1357 } else {
1358 eprintln!();
1359 let _ = (warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1360 }
1361 Ok(())
1362}
1363
1364fn check_control(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<bool, LuaError> {
1368 if tocont || message.first() != Some(&b'@') {
1369 return Ok(false);
1370 }
1371 let cmd = &message[1..];
1372 let _ = state;
1373 if cmd == b"off" {
1374 let _ = warn_off as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1375 } else if cmd == b"on" {
1376 let _ = warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1377 }
1378 Ok(true)
1379}
1380
1381pub fn check_version(state: &mut LuaState, ver: f64, sz: usize) -> Result<(), LuaError> {
1384 const LUAL_NUMSIZES: usize = std::mem::size_of::<i64>() * 16 + std::mem::size_of::<f64>();
1385 if sz != LUAL_NUMSIZES {
1386 return Err(LuaError::runtime(format_args!(
1387 "core and library have incompatible numeric types"
1388 )));
1389 }
1390 let v = state.lua_version();
1391 if (v - ver).abs() > f64::EPSILON {
1392 return Err(LuaError::runtime(format_args!(
1393 "version mismatch: app. needs {}, Lua core provides {}",
1394 ver, v
1395 )));
1396 }
1397 Ok(())
1398}
1399
1400struct BStr<'a>(&'a [u8]);
1408
1409impl<'a> std::fmt::Display for BStr<'a> {
1410 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1411 for &b in self.0 {
1412 if b.is_ascii() {
1413 f.write_char(b as char)?;
1414 } else {
1415 write!(f, "\\x{:02x}", b)?;
1416 }
1417 }
1418 Ok(())
1419 }
1420}
1421
1422use std::fmt::Write as _;
1424
1425