1use lua_types::{
22 error::LuaError,
23 value::LuaValue,
24 gc::GcRef,
25 string::LuaString,
26 userdata::LuaUserData,
27 LuaType,
28 LuaStatus,
29};
30use crate::state_stub::{LuaState, LuaStateStubExt as _, LuaDebug};
31
32const LEVELS1: i32 = 10;
36
37const LEVELS2: i32 = 11;
39
40const FREELIST_REF: i64 = 3; pub const LUA_REFNIL: i32 = -1;
46
47pub const LUA_NOREF: i32 = -2;
49
50pub const LUA_ERRFILE: i32 = 6;
52
53pub const LUA_LOADED_TABLE: &[u8] = b"_LOADED";
55
56pub const LUA_PRELOAD_TABLE: &[u8] = b"_PRELOAD";
58
59pub const LUA_GNAME: &[u8] = b"_G";
61
62pub const LUA_FILE_HANDLE: &[u8] = b"FILE*";
64
65const LUA_REGISTRYINDEX: i32 = -1_001_000;
67
68#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
70const LUA_MINSTACK: i32 = 20;
71
72pub struct LuaReg {
80 pub name: &'static [u8],
81 pub func: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
82}
83
84pub struct LuaBuffer {
91 pub data: Vec<u8>,
92}
93
94pub struct LuaStream {
101 pub f: Option<Box<dyn std::io::Read>>,
105 pub closef: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
107}
108
109fn find_field(
116 state: &mut LuaState,
117 objidx: i32,
118 level: i32,
119) -> Result<bool, LuaError> {
120 if level == 0 || state.type_at(-1) != LuaType::Table {
121 return Ok(false);
122 }
123 state.push(LuaValue::Nil);
124 while state.table_next(-2)? {
125 if state.type_at(-2) == LuaType::String {
126 if state.raw_equal(objidx, -1)? {
127 state.pop_n(1); return Ok(true);
129 } else if find_field(state, objidx, level - 1)? {
130 state.push_string(b".")?; state.replace(-3)?; state.concat(3)?; return Ok(true);
135 }
136 }
137 state.pop_n(1); }
139 Ok(false)
140}
141
142fn push_global_func_name(
146 state: &mut LuaState,
147 ar: &mut LuaDebug,
148) -> Result<bool, LuaError> {
149 let top = state.top_count();
150 state.get_info(b"f", ar)?;
151 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
152 check_stack(state, 6, Some(b"not enough stack"))?;
153 if find_field(state, top + 1, 2)? {
154 if state.peek_bytes(-1).map_or(false, |n| n.starts_with(b"_G.")) {
155 let suffix = state.peek_bytes(-1)
156 .map(|n| n[3..].to_vec())
157 .unwrap_or_default();
158 state.push_bytes(&suffix)?;
159 state.remove(-2)?;
160 }
161 state.copy_value(-1, top + 1)?;
162 lua_vm::api::set_top(state, top + 1)?;
163 Ok(true)
164 } else {
165 lua_vm::api::set_top(state, top)?;
166 Ok(false)
167 }
168}
169
170fn push_global_func_name_from_target(
171 state: &mut LuaState,
172 target: &mut LuaState,
173 ar: &mut LuaDebug,
174) -> Result<bool, LuaError> {
175 let top = state.top_count();
176 target.get_info(b"f", ar)?;
177 let func = target.get_at(target.top_idx() - 1);
178 target.pop_n(1);
179 state.push(func);
180 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
181 check_stack(state, 6, Some(b"not enough stack"))?;
182 if find_field(state, top + 1, 2)? {
183 if state.peek_bytes(-1).map_or(false, |n| n.starts_with(b"_G.")) {
184 let suffix = state.peek_bytes(-1)
185 .map(|n| n[3..].to_vec())
186 .unwrap_or_default();
187 state.push_bytes(&suffix)?;
188 state.remove(-2)?;
189 }
190 state.copy_value(-1, top + 1)?;
191 lua_vm::api::set_top(state, top + 1)?;
192 Ok(true)
193 } else {
194 lua_vm::api::set_top(state, top)?;
195 Ok(false)
196 }
197}
198
199fn push_func_name(
202 state: &mut LuaState,
203 ar: &mut LuaDebug,
204 global_lookup_target: Option<&mut LuaState>,
205) -> Result<(), LuaError> {
206 let found_global = match global_lookup_target {
207 Some(target) => push_global_func_name_from_target(state, target, ar)?,
208 None => push_global_func_name(state, ar)?,
209 };
210 if found_global {
211 let name = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
212 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
213 state.remove(-2)?;
214 } else if !ar.namewhat.is_empty() {
215 let namewhat = ar.namewhat.clone();
216 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
217 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
218 } else if ar.what == b'm' {
219 state.push_string(b"main chunk")?;
220 } else if ar.what != b'C' {
221 let src = ar.short_src.clone();
222 let line = ar.linedefined;
223 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
224 } else {
225 state.push_string(b"?")?;
226 }
227 Ok(())
228}
229
230fn last_level(state: &mut LuaState) -> i32 {
233 let mut ar = LuaDebug::default();
234 let mut li: i32 = 1;
235 let mut le: i32 = 1;
236 while state.get_stack(le, &mut ar) {
237 li = le;
238 le *= 2;
239 }
240 while li < le {
242 let m = (li + le) / 2;
243 if state.get_stack(m, &mut ar) {
244 li = m + 1;
245 } else {
246 le = m;
247 }
248 }
249 le - 1
250}
251
252pub fn traceback(
262 state: &mut LuaState,
263 mut other: Option<&mut LuaState>,
264 msg: Option<&[u8]>,
265 level: i32,
266) -> Result<(), LuaError> {
267 let mut b = LuaBuffer::new();
268 let mut ar = LuaDebug::default();
269 let last = match &mut other {
270 Some(o) => last_level(o),
271 None => last_level(state),
272 };
273 let mut limit2show: i32 = if last - level > LEVELS1 + LEVELS2 { LEVELS1 } else { -1 };
274 buf_init(state, &mut b);
275 if let Some(m) = msg {
276 add_lstring(&mut b, m);
277 add_char(&mut b, b'\n');
278 }
279 add_lstring(&mut b, b"stack traceback:");
280 let mut level = level;
281 loop {
282 let got = match &mut other {
283 Some(o) => o.get_stack(level, &mut ar),
284 None => state.get_stack(level, &mut ar),
285 };
286 if !got {
287 break;
288 }
289 level += 1;
290 if limit2show == 0 {
291 let n = last - level - LEVELS2 + 1;
292 state.push_fstring(format_args!("\n\t...\t(skipping {} levels)", n))?;
293 add_value(state, &mut b)?;
294 level += n;
295 limit2show = LEVELS2;
296 } else {
297 limit2show -= 1;
298 match &mut other {
299 Some(o) => o.get_info(b"Slnt", &mut ar)?,
300 None => state.get_info(b"Slnt", &mut ar)?,
301 }
302 if ar.currentline <= 0 {
303 let src = ar.short_src.clone();
304 state.push_fstring(format_args!("\n\t{}: in ", BStr(&src)))?;
305 } else {
306 let src = ar.short_src.clone();
307 let line = ar.currentline;
308 state.push_fstring(format_args!("\n\t{}:{}: in ", BStr(&src), line))?;
309 }
310 add_value(state, &mut b)?;
311 match &mut other {
312 Some(o) => push_func_name(state, &mut ar, Some(&mut **o))?,
313 None => push_func_name(state, &mut ar, None)?,
314 }
315 add_value(state, &mut b)?;
316 if ar.istailcall {
317 add_lstring(&mut b, b"\n\t(...tail calls...)");
318 }
319 }
320 }
321 push_result(state, &mut b)?;
322 Ok(())
323}
324
325pub fn arg_error(
332 state: &mut LuaState,
333 mut arg: i32,
334 extramsg: &[u8],
335) -> Result<usize, LuaError> {
336 let mut ar = LuaDebug::default();
337 if !state.get_stack(0, &mut ar) {
338 return Err(LuaError::runtime(format_args!(
339 "bad argument #{} ({})",
340 arg,
341 BStr(extramsg)
342 )));
343 }
344 state.get_info(b"n", &mut ar)?;
345 if ar.namewhat == b"method" {
346 arg -= 1; if arg == 0 {
348 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
349 return Err(LuaError::runtime(format_args!(
350 "calling '{}' on bad self ({})",
351 BStr(&name),
352 BStr(extramsg)
353 )));
354 }
355 }
356 let fname = if ar.name.is_none() {
357 if push_global_func_name(state, &mut ar)? {
358 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
359 } else {
360 b"?".to_vec()
361 }
362 } else {
363 ar.name.clone().unwrap_or_else(|| b"?".to_vec())
364 };
365 Err(LuaError::runtime(format_args!(
366 "bad argument #{} to '{}' ({})",
367 arg,
368 BStr(&fname),
369 BStr(extramsg)
370 )))
371}
372
373pub fn type_error_arg(
377 state: &mut LuaState,
378 arg: i32,
379 tname: &[u8],
380) -> Result<usize, LuaError> {
381 let typearg: Vec<u8> = if get_metafield(state, arg, b"__name")? == LuaType::String {
387 let bytes = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
388 state.pop_n(1);
389 bytes
390 } else if state.type_at(arg) == LuaType::LightUserData {
391 b"light userdata".to_vec()
392 } else {
393 state.type_name_at(arg).to_vec()
394 };
395 let msg_owned = format!(
396 "{} expected, got {}",
397 BStr(tname),
398 BStr(&typearg)
399 );
400 arg_error(state, arg, msg_owned.as_bytes())
401}
402
403fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
406 let name = state.type_name(tag);
407 type_error_arg(state, arg, name)?;
408 Ok(())
409}
410
411pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
415 let mut ar = LuaDebug::default();
416 if state.get_stack(level, &mut ar) {
417 state.get_info(b"Sl", &mut ar)?;
418 if ar.currentline > 0 {
419 let src = ar.short_src.clone();
420 let line = ar.currentline;
421 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
422 return Ok(());
423 }
424 }
425 state.push_string(b"")?;
426 Ok(())
427}
428
429pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
436 push_where(state, 1)?;
437 let where_str = state.pop_bytes();
438 let full = [where_str.as_slice(), msg].concat();
439 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
440}
441
442pub fn file_result(
447 state: &mut LuaState,
448 stat: bool,
449 fname: Option<&[u8]>,
450) -> Result<usize, LuaError> {
451 if stat {
452 state.push(LuaValue::Bool(true));
453 Ok(1)
454 } else {
455 state.push(LuaValue::Nil);
456 let errmsg = b"(errno unavailable in Rust port)".to_vec();
458 if let Some(name) = fname {
459 let full = [name, b": ".as_slice(), &errmsg].concat();
460 state.push_bytes(&full)?;
461 } else {
462 state.push_bytes(&errmsg)?;
463 }
464 state.push(LuaValue::Int(0));
466 Ok(3)
467 }
468}
469
470pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
475 if stat != 0 {
476 return file_result(state, false, None);
477 }
478 let what = b"exit".as_slice();
479 state.push(LuaValue::Bool(true));
480 state.push_bytes(what)?;
481 state.push(LuaValue::Int(stat as i64));
482 Ok(3)
483}
484
485pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
492 if get_metatable(state, tname)? != LuaType::Nil {
493 return Ok(false); }
495 state.pop_n(1);
496 state.create_table(0, 2)?;
497 state.push_bytes(tname)?;
498 state.set_field(-2, b"__name")?;
499 state.push_value(-1)?;
500 state.set_field(LUA_REGISTRYINDEX, tname)?;
501 Ok(true)
502}
503
504pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
507 get_metatable(state, tname)?;
508 state.set_metatable(-2)?;
509 Ok(())
510}
511
512pub fn test_udata(
516 state: &mut LuaState,
517 ud: i32,
518 tname: &[u8],
519) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
520 let p = state.to_userdata(ud);
521 if let Some(p) = p {
522 if state.get_metatable(ud)? {
523 get_metatable(state, tname)?;
524 let eq = state.raw_equal(-1, -2)?;
525 state.pop_n(2); if eq {
527 return Ok(Some(p));
528 }
529 }
530 }
531 Ok(None)
532}
533
534pub fn check_udata(
537 state: &mut LuaState,
538 ud: i32,
539 tname: &[u8],
540) -> Result<GcRef<LuaUserData>, LuaError> {
541 match test_udata(state, ud, tname)? {
542 Some(p) => Ok(p),
543 None => {
544 type_error_arg(state, ud, tname)?;
545 unreachable!()
546 }
547 }
548}
549
550pub fn check_option(
556 state: &mut LuaState,
557 arg: i32,
558 def: Option<&[u8]>,
559 lst: &[&[u8]],
560) -> Result<usize, LuaError> {
561 let name: Vec<u8> = match def {
562 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
563 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
564 };
565 for (i, entry) in lst.iter().enumerate() {
566 if *entry == name.as_slice() {
567 return Ok(i);
568 }
569 }
570 Err(LuaError::runtime(format_args!(
571 "invalid option '{}'",
572 BStr(&name)
573 )))
574}
575
576pub fn check_stack(
579 state: &mut LuaState,
580 space: i32,
581 msg: Option<&[u8]>,
582) -> Result<(), LuaError> {
583 if !state.check_stack_space(space) {
584 match msg {
585 Some(m) => {
586 return Err(LuaError::runtime(format_args!(
587 "stack overflow ({})",
588 BStr(m)
589 )));
590 }
591 None => {
592 return Err(LuaError::runtime(format_args!("stack overflow")));
593 }
594 }
595 }
596 Ok(())
597}
598
599pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
602 if state.type_at(arg) != t {
603 tag_error(state, arg, t)?;
604 }
605 Ok(())
606}
607
608pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
611 if state.type_at(arg) == LuaType::None {
612 return Err(LuaError::arg_error(arg, "value expected"));
613 }
614 Ok(())
615}
616
617pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
620 match state.to_lua_string(arg) {
621 Some(s) => Ok(s),
622 None => {
623 tag_error(state, arg, LuaType::String)?;
624 unreachable!()
625 }
626 }
627}
628
629pub fn opt_lstring(
632 state: &mut LuaState,
633 arg: i32,
634 def: Option<&[u8]>,
635) -> Result<Option<Vec<u8>>, LuaError> {
636 if state.is_none_or_nil(arg) {
637 return Ok(def.map(|d| d.to_vec()));
638 }
639 let s = check_lstring(state, arg)?;
640 Ok(Some(s.as_bytes().to_vec()))
641}
642
643pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
646 match state.to_number_x(arg) {
647 Some(d) => Ok(d),
648 None => {
649 tag_error(state, arg, LuaType::Number)?;
650 unreachable!()
651 }
652 }
653}
654
655pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
658 if state.is_none_or_nil(arg) {
659 Ok(def)
660 } else {
661 check_number(state, arg)
662 }
663}
664
665fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
671 if state.is_number(arg) {
672 Err(LuaError::arg_error(
673 arg,
674 "number has no integer representation",
675 ))
676 } else {
677 tag_error(state, arg, LuaType::Number)?;
678 unreachable!("tag_error always returns Err")
679 }
680}
681
682pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
685 match state.to_integer_x(arg) {
686 Some(d) => Ok(d),
687 None => {
688 int_error(state, arg)?;
689 unreachable!("int_error always returns Err")
690 }
691 }
692}
693
694pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
697 if state.is_none_or_nil(arg) {
698 Ok(def)
699 } else {
700 check_integer(state, arg)
701 }
702}
703
704impl LuaBuffer {
707 pub fn new() -> Self {
711 LuaBuffer { data: Vec::new() }
712 }
713
714 pub fn len(&self) -> usize {
716 self.data.len()
717 }
718}
719
720impl Default for LuaBuffer {
721 fn default() -> Self {
722 LuaBuffer::new()
723 }
724}
725
726pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
732 *buf = LuaBuffer::new();
736 let _ = state.push(LuaValue::Nil);
738}
739
740pub fn buf_init_size(
743 state: &mut LuaState,
744 buf: &mut LuaBuffer,
745 sz: usize,
746) -> Result<(), LuaError> {
747 buf_init(state, buf);
748 buf.data.reserve(sz);
749 Ok(())
750}
751
752fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
756 if usize::MAX - sz < buf.len() {
757 return Err(LuaError::runtime(format_args!("buffer too large")));
758 }
759 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
761 Ok(buf.len() + sz)
762 } else {
763 Ok(newsize)
764 }
765}
766
767pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
770 if buf.data.capacity() - buf.data.len() < sz {
771 let newcap = new_buff_size(buf, sz)?;
772 buf.data.reserve(newcap - buf.data.len());
773 }
774 Ok(())
775}
776
777pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
780 if !s.is_empty() {
781 buf.data.extend_from_slice(s);
782 }
783}
784
785pub fn add_char(buf: &mut LuaBuffer, c: u8) {
788 buf.data.push(c);
789}
790
791pub fn add_size(_buf: &mut LuaBuffer, sz: usize) {
794 let _ = sz;
799}
800
801pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
804 if let Some(bytes) = state.peek_bytes(-1) {
805 let owned = bytes.to_vec();
806 add_lstring(buf, &owned);
807 }
808 state.pop_n(1);
809 Ok(())
810}
811
812pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
815 state.push_bytes(&buf.data)?;
816 state.remove(-2)?;
817 Ok(())
818}
819
820pub fn push_result_size(
823 state: &mut LuaState,
824 buf: &mut LuaBuffer,
825 sz: usize,
826) -> Result<(), LuaError> {
827 add_size(buf, sz);
828 push_result(state, buf)
829}
830
831pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
835 if pat.is_empty() {
836 add_lstring(buf, s);
837 return;
838 }
839 let mut remaining = s;
840 while let Some(pos) = find_bytes(remaining, pat) {
841 add_lstring(buf, &remaining[..pos]);
842 add_lstring(buf, repl);
843 remaining = &remaining[pos + pat.len()..];
844 }
845 add_lstring(buf, remaining);
846}
847
848pub fn gsub<'a>(
852 state: &'a mut LuaState,
853 s: &[u8],
854 pat: &[u8],
855 repl: &[u8],
856) -> Result<Vec<u8>, LuaError> {
857 let mut b = LuaBuffer::new();
858 buf_init(state, &mut b);
859 add_gsub(&mut b, s, pat, repl);
860 push_result(state, &mut b)?;
861 Ok(state.peek_bytes(-1).unwrap_or_default())
862}
863
864fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
868 if needle.is_empty() {
869 return Some(0);
870 }
871 haystack.windows(needle.len()).position(|w| w == needle)
872}
873
874pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
881 if state.type_at(-1) == LuaType::Nil {
882 state.pop_n(1);
883 return Ok(LUA_REFNIL);
884 }
885 let t = state.abs_index(t);
886 let ref_val: i32;
887 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
888 ref_val = 0; state.push(LuaValue::Int(0));
890 state.raw_set_i(t, FREELIST_REF)?;
891 } else {
892 debug_assert!(state.type_at(-1) == LuaType::Number);
893 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
894 }
895 state.pop_n(1); let next_ref: i32;
897 if ref_val != 0 {
898 state.raw_get_i(t, ref_val as i64)?;
899 state.raw_set_i(t, FREELIST_REF)?;
900 next_ref = ref_val;
901 } else {
902 next_ref = (state.raw_len(t) as i32) + 1;
903 }
904 state.raw_set_i(t, next_ref as i64)?;
905 Ok(next_ref)
906}
907
908pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
911 if r >= 0 {
912 let t = state.abs_index(t);
913 state.raw_get_i(t, FREELIST_REF)?;
914 debug_assert!(state.type_at(-1) == LuaType::Number);
915 state.raw_set_i(t, r as i64)?;
916 state.push(LuaValue::Int(r as i64));
917 state.raw_set_i(t, FREELIST_REF)?;
918 }
919 Ok(())
920}
921
922fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
927 let mut remaining = Some(data);
928 move || remaining.take()
929}
930
931fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
940 let s = if buf.starts_with(b"\xEF\xBB\xBF") { &buf[3..] } else { buf };
941 if s.first() == Some(&b'#') {
942 let nl = s.iter().position(|&b| b == b'\n').map(|p| p + 1).unwrap_or(s.len());
943 let rest = &s[nl..];
944 if rest.first() == Some(&0x1B) {
945 rest.to_vec()
946 } else {
947 let mut out = Vec::with_capacity(rest.len() + 1);
948 out.push(b'\n');
949 out.extend_from_slice(rest);
950 out
951 }
952 } else {
953 s.to_vec()
954 }
955}
956
957pub fn load_filex(
967 state: &mut LuaState,
968 filename: Option<&[u8]>,
969 mode: Option<&[u8]>,
970) -> Result<i32, LuaError> {
971 let _ = mode;
972 let fname = match filename {
973 Some(f) => f,
974 None => {
975 state.push_string(b"cannot read stdin: no filename given")?;
978 return Ok(LUA_ERRFILE);
979 }
980 };
981 let raw = match state.global().file_loader_hook {
982 Some(load_fn) => load_fn(fname),
983 None => Err(LuaError::runtime(format_args!(
984 "no file_loader_hook registered"
985 ))),
986 };
987 let raw = match raw {
988 Ok(bytes) => bytes,
989 Err(e) => {
990 let detail = match &e {
991 LuaError::Runtime(LuaValue::Str(s)) => {
992 String::from_utf8_lossy(s.as_bytes()).into_owned()
993 }
994 other => format!("{:?}", other),
995 };
996 state.push_fstring(format_args!(
997 "cannot open {}: {}",
998 BStr(fname),
999 detail
1000 ))?;
1001 return Ok(LUA_ERRFILE);
1002 }
1003 };
1004 let payload = skip_bom_and_shebang(&raw);
1005 let mut once = Some(payload);
1006 let boxed: Box<dyn FnMut() -> Option<Vec<u8>>> =
1007 Box::new(move || once.take());
1008 let mut chunkname = b"@".to_vec();
1009 chunkname.extend_from_slice(fname);
1010 let status = lua_vm::api::load(state, boxed, Some(&chunkname), mode)?;
1011 Ok(if status == LuaStatus::Ok { 0 } else { status as i32 })
1012}
1013
1014pub fn load_bufferx(
1017 state: &mut LuaState,
1018 buff: &[u8],
1019 name: &[u8],
1020 mode: Option<&[u8]>,
1021) -> Result<i32, LuaError> {
1022 let _reader = make_string_reader(buff.to_vec());
1024 let ok = state.load(buff, name, mode)?;
1025 Ok(if ok { 0 } else { 1 })
1026}
1027
1028pub fn load_buffer(
1031 state: &mut LuaState,
1032 buff: &[u8],
1033 name: &[u8],
1034) -> Result<i32, LuaError> {
1035 load_bufferx(state, buff, name, None)
1036}
1037
1038pub fn load_string(state: &mut LuaState, s: &[u8]) -> Result<i32, LuaError> {
1041 load_buffer(state, s, s)
1042}
1043
1044pub fn get_metafield(
1050 state: &mut LuaState,
1051 obj: i32,
1052 event: &[u8],
1053) -> Result<LuaType, LuaError> {
1054 if !state.get_metatable(obj)? {
1055 return Ok(LuaType::Nil);
1056 }
1057 state.push_bytes(event)?;
1058 let tt = state.raw_get(-2)?;
1059 if tt == LuaType::Nil {
1060 state.pop_n(2);
1061 } else {
1062 state.remove(-2)?;
1063 }
1064 Ok(tt)
1065}
1066
1067pub fn call_meta(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<bool, LuaError> {
1071 let obj = state.abs_index(obj);
1072 if get_metafield(state, obj, event)? == LuaType::Nil {
1073 return Ok(false);
1074 }
1075 state.push_value(obj)?;
1076 state.call(1, 1)?;
1077 Ok(true)
1078}
1079
1080pub fn lua_len(state: &mut LuaState, idx: i32) -> Result<i64, LuaError> {
1084 state.len_op(idx)?;
1085 let l = match state.to_integer_x(-1) {
1086 Some(n) => n,
1087 None => {
1088 return Err(LuaError::runtime(format_args!(
1089 "object length is not an integer"
1090 )));
1091 }
1092 };
1093 state.pop_n(1);
1094 Ok(l)
1095}
1096
1097pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Vec<u8>, LuaError> {
1101 let idx = state.abs_index(idx);
1102 if call_meta(state, idx, b"__tostring")? {
1103 if state.type_at(-1) != LuaType::String {
1104 return Err(LuaError::runtime(format_args!(
1105 "'__tostring' must return a string"
1106 )));
1107 }
1108 } else {
1109 match state.type_at(idx) {
1110 LuaType::Number => {
1111 if state.is_integer(idx) {
1112 let i = state.to_integer_x(idx).unwrap_or(0);
1113 state.push_fstring(format_args!("{}", i))?;
1114 } else {
1115 let f = state.to_number_x(idx).unwrap_or(0.0);
1116 state.push_fstring(format_args!("{:?}", f))?;
1117 }
1118 }
1119 LuaType::String => {
1120 state.push_value(idx)?;
1121 }
1122 LuaType::Boolean => {
1123 let b = state.to_boolean(idx);
1124 state.push_string(if b { b"true" } else { b"false" })?;
1125 }
1126 LuaType::Nil => {
1127 state.push_string(b"nil")?;
1128 }
1129 _ => {
1130 let tt = get_metafield(state, idx, b"__name")?;
1131 let kind: Vec<u8> = if tt == LuaType::String {
1132 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1133 } else {
1134 state.type_name_at(idx).to_vec()
1135 };
1136 state.push_fstring(format_args!("{}: 0x?", BStr(&kind)))?;
1139 if tt != LuaType::Nil {
1140 state.remove(-2)?;
1141 }
1142 }
1143 }
1144 }
1145 Ok(state.peek_bytes(-1).unwrap_or_default())
1146}
1147
1148pub fn set_funcs(
1152 state: &mut LuaState,
1153 l: &[LuaReg],
1154 nup: i32,
1155) -> Result<(), LuaError> {
1156 check_stack(state, nup, Some(b"too many upvalues"))?;
1157 for reg in l {
1158 match reg.func {
1159 None => {
1160 state.push(LuaValue::Bool(false));
1161 }
1162 Some(f) => {
1163 for _ in 0..nup {
1164 state.push_value(-nup)?;
1165 }
1166 state.push_c_closure(f, nup)?;
1167 }
1168 }
1169 state.set_field(-(nup + 2), reg.name)?;
1170 }
1171 state.pop_n(nup as usize);
1172 Ok(())
1173}
1174
1175pub fn get_subtable(
1179 state: &mut LuaState,
1180 idx: i32,
1181 fname: &[u8],
1182) -> Result<bool, LuaError> {
1183 if state.get_field(idx, fname)? == LuaType::Table {
1184 return Ok(true);
1185 }
1186 state.pop_n(1);
1187 let idx = state.abs_index(idx);
1188 let new_tbl = state.new_table();
1189 state.push(LuaValue::Table(new_tbl));
1190 state.push_value(-1)?;
1191 state.set_field(idx, fname)?;
1192 Ok(false)
1193}
1194
1195pub fn requiref(
1200 state: &mut LuaState,
1201 modname: &[u8],
1202 openf: fn(&mut LuaState) -> Result<usize, LuaError>,
1203 glb: bool,
1204) -> Result<(), LuaError> {
1205 get_subtable(state, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
1206 state.get_field(-1, modname)?;
1207 if !state.to_boolean(-1) {
1208 state.pop_n(1);
1209 state.push_c_function(openf)?;
1210 state.push_bytes(modname)?;
1211 state.call(1, 1)?;
1212 state.push_value(-1)?;
1213 state.set_field(-3, modname)?;
1214 }
1215 state.remove(-2)?;
1216 if glb {
1217 state.push_value(-1)?;
1218 state.set_global(modname)?;
1219 }
1220 Ok(())
1221}
1222
1223pub fn get_metatable(state: &mut LuaState, tname: &[u8]) -> Result<LuaType, LuaError> {
1228 state.get_field(LUA_REGISTRYINDEX, tname)
1229}
1230
1231pub fn new_state() -> Result<LuaState, LuaError> {
1237 let _ = default_panic_handler;
1240 let _ = warn_off;
1241 todo!("phase-b: LuaState::new()")
1242}
1243
1244fn default_panic_handler(state: &mut LuaState) -> Result<usize, LuaError> {
1247 let msg = if state.type_at(-1) == LuaType::String {
1248 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1249 } else {
1250 b"error object is not a string".to_vec()
1251 };
1252 eprintln!("PANIC: unprotected error in call to Lua API ({})", BStr(&msg));
1253 Ok(0) }
1255
1256fn warn_off(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1259 check_control(state, message, tocont)?;
1260 Ok(())
1261}
1262
1263fn warn_on(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1266 if check_control(state, message, tocont)? {
1267 return Ok(());
1268 }
1269 eprint!("Lua warning: ");
1270 warn_cont(state, message, tocont)
1271}
1272
1273fn warn_cont(_state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1276 eprint!("{}", BStr(message));
1277 if tocont {
1279 let _ = (warn_cont as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1280 } else {
1281 eprintln!();
1282 let _ = (warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1283 }
1284 Ok(())
1285}
1286
1287fn check_control(
1291 state: &mut LuaState,
1292 message: &[u8],
1293 tocont: bool,
1294) -> Result<bool, LuaError> {
1295 if tocont || message.first() != Some(&b'@') {
1296 return Ok(false);
1297 }
1298 let cmd = &message[1..];
1299 let _ = state;
1301 if cmd == b"off" {
1302 let _ = warn_off as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1303 } else if cmd == b"on" {
1304 let _ = warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1305 }
1306 Ok(true)
1307}
1308
1309pub fn check_version(state: &mut LuaState, ver: f64, sz: usize) -> Result<(), LuaError> {
1312 const LUAL_NUMSIZES: usize = std::mem::size_of::<i64>() * 16 + std::mem::size_of::<f64>();
1313 if sz != LUAL_NUMSIZES {
1314 return Err(LuaError::runtime(format_args!(
1315 "core and library have incompatible numeric types"
1316 )));
1317 }
1318 let v = state.lua_version();
1319 if (v - ver).abs() > f64::EPSILON {
1320 return Err(LuaError::runtime(format_args!(
1321 "version mismatch: app. needs {}, Lua core provides {}",
1322 ver, v
1323 )));
1324 }
1325 Ok(())
1326}
1327
1328struct BStr<'a>(&'a [u8]);
1336
1337impl<'a> std::fmt::Display for BStr<'a> {
1338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1339 for &b in self.0 {
1340 if b.is_ascii() {
1341 f.write_char(b as char)?;
1342 } else {
1343 write!(f, "\\x{:02x}", b)?;
1344 }
1345 }
1346 Ok(())
1347 }
1348}
1349
1350use std::fmt::Write as _;
1352
1353