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 if state.type_at(arg) == LuaType::None {
393 b"no value".to_vec()
394 } else {
395 state.type_name_at(arg).to_vec()
396 };
397 let msg_owned = format!(
398 "{} expected, got {}",
399 BStr(tname),
400 BStr(&typearg)
401 );
402 arg_error(state, arg, msg_owned.as_bytes())
403}
404
405fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
408 let name = state.type_name(tag);
409 type_error_arg(state, arg, name)?;
410 Ok(())
411}
412
413pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
417 let mut ar = LuaDebug::default();
418 if state.get_stack(level, &mut ar) {
419 state.get_info(b"Sl", &mut ar)?;
420 if ar.currentline > 0 {
421 let src = ar.short_src.clone();
422 let line = ar.currentline;
423 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
424 return Ok(());
425 }
426 }
427 state.push_string(b"")?;
428 Ok(())
429}
430
431pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
438 push_where(state, 1)?;
439 let where_str = state.pop_bytes();
440 let full = [where_str.as_slice(), msg].concat();
441 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
442}
443
444pub fn file_result(
449 state: &mut LuaState,
450 stat: bool,
451 fname: Option<&[u8]>,
452) -> Result<usize, LuaError> {
453 if stat {
454 state.push(LuaValue::Bool(true));
455 Ok(1)
456 } else {
457 state.push(LuaValue::Nil);
458 let errmsg = b"(errno unavailable in Rust port)".to_vec();
460 if let Some(name) = fname {
461 let full = [name, b": ".as_slice(), &errmsg].concat();
462 state.push_bytes(&full)?;
463 } else {
464 state.push_bytes(&errmsg)?;
465 }
466 state.push(LuaValue::Int(0));
468 Ok(3)
469 }
470}
471
472pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
477 if stat != 0 {
478 return file_result(state, false, None);
479 }
480 let what = b"exit".as_slice();
481 state.push(LuaValue::Bool(true));
482 state.push_bytes(what)?;
483 state.push(LuaValue::Int(stat as i64));
484 Ok(3)
485}
486
487pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
494 if get_metatable(state, tname)? != LuaType::Nil {
495 return Ok(false); }
497 state.pop_n(1);
498 state.create_table(0, 2)?;
499 state.push_bytes(tname)?;
500 state.set_field(-2, b"__name")?;
501 state.push_value(-1)?;
502 state.set_field(LUA_REGISTRYINDEX, tname)?;
503 Ok(true)
504}
505
506pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
509 get_metatable(state, tname)?;
510 state.set_metatable(-2)?;
511 Ok(())
512}
513
514pub fn test_udata(
518 state: &mut LuaState,
519 ud: i32,
520 tname: &[u8],
521) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
522 let p = state.to_userdata(ud);
523 if let Some(p) = p {
524 if state.get_metatable(ud)? {
525 get_metatable(state, tname)?;
526 let eq = state.raw_equal(-1, -2)?;
527 state.pop_n(2); if eq {
529 return Ok(Some(p));
530 }
531 }
532 }
533 Ok(None)
534}
535
536pub fn check_udata(
539 state: &mut LuaState,
540 ud: i32,
541 tname: &[u8],
542) -> Result<GcRef<LuaUserData>, LuaError> {
543 match test_udata(state, ud, tname)? {
544 Some(p) => Ok(p),
545 None => {
546 type_error_arg(state, ud, tname)?;
547 unreachable!()
548 }
549 }
550}
551
552pub fn check_option(
558 state: &mut LuaState,
559 arg: i32,
560 def: Option<&[u8]>,
561 lst: &[&[u8]],
562) -> Result<usize, LuaError> {
563 let name: Vec<u8> = match def {
564 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
565 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
566 };
567 for (i, entry) in lst.iter().enumerate() {
568 if *entry == name.as_slice() {
569 return Ok(i);
570 }
571 }
572 Err(LuaError::runtime(format_args!(
573 "invalid option '{}'",
574 BStr(&name)
575 )))
576}
577
578pub fn check_stack(
581 state: &mut LuaState,
582 space: i32,
583 msg: Option<&[u8]>,
584) -> Result<(), LuaError> {
585 if !state.check_stack_space(space) {
586 match msg {
587 Some(m) => {
588 return Err(LuaError::runtime(format_args!(
589 "stack overflow ({})",
590 BStr(m)
591 )));
592 }
593 None => {
594 return Err(LuaError::runtime(format_args!("stack overflow")));
595 }
596 }
597 }
598 Ok(())
599}
600
601pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
604 if state.type_at(arg) != t {
605 tag_error(state, arg, t)?;
606 }
607 Ok(())
608}
609
610pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
613 if state.type_at(arg) == LuaType::None {
614 return Err(LuaError::arg_error(arg, "value expected"));
615 }
616 Ok(())
617}
618
619pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
622 match state.to_lua_string(arg) {
623 Some(s) => Ok(s),
624 None => {
625 tag_error(state, arg, LuaType::String)?;
626 unreachable!()
627 }
628 }
629}
630
631pub fn opt_lstring(
634 state: &mut LuaState,
635 arg: i32,
636 def: Option<&[u8]>,
637) -> Result<Option<Vec<u8>>, LuaError> {
638 if state.is_none_or_nil(arg) {
639 return Ok(def.map(|d| d.to_vec()));
640 }
641 let s = check_lstring(state, arg)?;
642 Ok(Some(s.as_bytes().to_vec()))
643}
644
645pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
648 match state.to_number_x(arg) {
649 Some(d) => Ok(d),
650 None => {
651 tag_error(state, arg, LuaType::Number)?;
652 unreachable!()
653 }
654 }
655}
656
657pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
660 if state.is_none_or_nil(arg) {
661 Ok(def)
662 } else {
663 check_number(state, arg)
664 }
665}
666
667fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
673 if state.is_number(arg) {
674 Err(LuaError::arg_error(
675 arg,
676 "number has no integer representation",
677 ))
678 } else {
679 tag_error(state, arg, LuaType::Number)?;
680 unreachable!("tag_error always returns Err")
681 }
682}
683
684pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
687 match state.to_integer_x(arg) {
688 Some(d) => Ok(d),
689 None => {
690 int_error(state, arg)?;
691 unreachable!("int_error always returns Err")
692 }
693 }
694}
695
696pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
699 if state.is_none_or_nil(arg) {
700 Ok(def)
701 } else {
702 check_integer(state, arg)
703 }
704}
705
706impl LuaBuffer {
709 pub fn new() -> Self {
713 LuaBuffer { data: Vec::new() }
714 }
715
716 pub fn len(&self) -> usize {
718 self.data.len()
719 }
720}
721
722impl Default for LuaBuffer {
723 fn default() -> Self {
724 LuaBuffer::new()
725 }
726}
727
728pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
734 *buf = LuaBuffer::new();
738 let _ = state.push(LuaValue::Nil);
740}
741
742pub fn buf_init_size(
745 state: &mut LuaState,
746 buf: &mut LuaBuffer,
747 sz: usize,
748) -> Result<(), LuaError> {
749 buf_init(state, buf);
750 buf.data.reserve(sz);
751 Ok(())
752}
753
754fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
758 if usize::MAX - sz < buf.len() {
759 return Err(LuaError::runtime(format_args!("buffer too large")));
760 }
761 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
763 Ok(buf.len() + sz)
764 } else {
765 Ok(newsize)
766 }
767}
768
769pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
772 if buf.data.capacity() - buf.data.len() < sz {
773 let newcap = new_buff_size(buf, sz)?;
774 buf.data.reserve(newcap - buf.data.len());
775 }
776 Ok(())
777}
778
779pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
782 if !s.is_empty() {
783 buf.data.extend_from_slice(s);
784 }
785}
786
787pub fn add_char(buf: &mut LuaBuffer, c: u8) {
790 buf.data.push(c);
791}
792
793pub fn add_size(_buf: &mut LuaBuffer, sz: usize) {
796 let _ = sz;
801}
802
803pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
806 if let Some(bytes) = state.peek_bytes(-1) {
807 let owned = bytes.to_vec();
808 add_lstring(buf, &owned);
809 }
810 state.pop_n(1);
811 Ok(())
812}
813
814pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
817 state.push_bytes(&buf.data)?;
818 state.remove(-2)?;
819 Ok(())
820}
821
822pub fn push_result_size(
825 state: &mut LuaState,
826 buf: &mut LuaBuffer,
827 sz: usize,
828) -> Result<(), LuaError> {
829 add_size(buf, sz);
830 push_result(state, buf)
831}
832
833pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
837 if pat.is_empty() {
838 add_lstring(buf, s);
839 return;
840 }
841 let mut remaining = s;
842 while let Some(pos) = find_bytes(remaining, pat) {
843 add_lstring(buf, &remaining[..pos]);
844 add_lstring(buf, repl);
845 remaining = &remaining[pos + pat.len()..];
846 }
847 add_lstring(buf, remaining);
848}
849
850pub fn gsub<'a>(
854 state: &'a mut LuaState,
855 s: &[u8],
856 pat: &[u8],
857 repl: &[u8],
858) -> Result<Vec<u8>, LuaError> {
859 let mut b = LuaBuffer::new();
860 buf_init(state, &mut b);
861 add_gsub(&mut b, s, pat, repl);
862 push_result(state, &mut b)?;
863 Ok(state.peek_bytes(-1).unwrap_or_default())
864}
865
866fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
870 if needle.is_empty() {
871 return Some(0);
872 }
873 haystack.windows(needle.len()).position(|w| w == needle)
874}
875
876pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
883 if state.type_at(-1) == LuaType::Nil {
884 state.pop_n(1);
885 return Ok(LUA_REFNIL);
886 }
887 let t = state.abs_index(t);
888 let ref_val: i32;
889 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
890 ref_val = 0; state.push(LuaValue::Int(0));
892 state.raw_set_i(t, FREELIST_REF)?;
893 } else {
894 debug_assert!(state.type_at(-1) == LuaType::Number);
895 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
896 }
897 state.pop_n(1); let next_ref: i32;
899 if ref_val != 0 {
900 state.raw_get_i(t, ref_val as i64)?;
901 state.raw_set_i(t, FREELIST_REF)?;
902 next_ref = ref_val;
903 } else {
904 next_ref = (state.raw_len(t) as i32) + 1;
905 }
906 state.raw_set_i(t, next_ref as i64)?;
907 Ok(next_ref)
908}
909
910pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
913 if r >= 0 {
914 let t = state.abs_index(t);
915 state.raw_get_i(t, FREELIST_REF)?;
916 debug_assert!(state.type_at(-1) == LuaType::Number);
917 state.raw_set_i(t, r as i64)?;
918 state.push(LuaValue::Int(r as i64));
919 state.raw_set_i(t, FREELIST_REF)?;
920 }
921 Ok(())
922}
923
924fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
929 let mut remaining = Some(data);
930 move || remaining.take()
931}
932
933fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
942 let s = if buf.starts_with(b"\xEF\xBB\xBF") { &buf[3..] } else { buf };
943 if s.first() == Some(&b'#') {
944 let nl = s.iter().position(|&b| b == b'\n').map(|p| p + 1).unwrap_or(s.len());
945 let rest = &s[nl..];
946 if rest.first() == Some(&0x1B) {
947 rest.to_vec()
948 } else {
949 let mut out = Vec::with_capacity(rest.len() + 1);
950 out.push(b'\n');
951 out.extend_from_slice(rest);
952 out
953 }
954 } else {
955 s.to_vec()
956 }
957}
958
959pub fn load_filex(
969 state: &mut LuaState,
970 filename: Option<&[u8]>,
971 mode: Option<&[u8]>,
972) -> Result<i32, LuaError> {
973 let _ = mode;
974 let fname = match filename {
975 Some(f) => f,
976 None => {
977 state.push_string(b"cannot read stdin: no filename given")?;
980 return Ok(LUA_ERRFILE);
981 }
982 };
983 let raw = match state.global().file_loader_hook {
984 Some(load_fn) => load_fn(fname),
985 None => Err(LuaError::runtime(format_args!(
986 "no file_loader_hook registered"
987 ))),
988 };
989 let raw = match raw {
990 Ok(bytes) => bytes,
991 Err(e) => {
992 let detail = match &e {
993 LuaError::Runtime(LuaValue::Str(s)) => {
994 String::from_utf8_lossy(s.as_bytes()).into_owned()
995 }
996 other => format!("{:?}", other),
997 };
998 state.push_fstring(format_args!(
999 "cannot open {}: {}",
1000 BStr(fname),
1001 detail
1002 ))?;
1003 return Ok(LUA_ERRFILE);
1004 }
1005 };
1006 let payload = skip_bom_and_shebang(&raw);
1007 let mut once = Some(payload);
1008 let boxed: Box<dyn FnMut() -> Option<Vec<u8>>> =
1009 Box::new(move || once.take());
1010 let mut chunkname = b"@".to_vec();
1011 chunkname.extend_from_slice(fname);
1012 let status = lua_vm::api::load(state, boxed, Some(&chunkname), mode)?;
1013 Ok(if status == LuaStatus::Ok { 0 } else { status as i32 })
1014}
1015
1016pub fn load_bufferx(
1019 state: &mut LuaState,
1020 buff: &[u8],
1021 name: &[u8],
1022 mode: Option<&[u8]>,
1023) -> Result<i32, LuaError> {
1024 let _reader = make_string_reader(buff.to_vec());
1026 let ok = state.load(buff, name, mode)?;
1027 Ok(if ok { 0 } else { 1 })
1028}
1029
1030pub fn load_buffer(
1033 state: &mut LuaState,
1034 buff: &[u8],
1035 name: &[u8],
1036) -> Result<i32, LuaError> {
1037 load_bufferx(state, buff, name, None)
1038}
1039
1040pub fn load_string(state: &mut LuaState, s: &[u8]) -> Result<i32, LuaError> {
1043 load_buffer(state, s, s)
1044}
1045
1046pub fn get_metafield(
1052 state: &mut LuaState,
1053 obj: i32,
1054 event: &[u8],
1055) -> Result<LuaType, LuaError> {
1056 if !state.get_metatable(obj)? {
1057 return Ok(LuaType::Nil);
1058 }
1059 state.push_bytes(event)?;
1060 let tt = state.raw_get(-2)?;
1061 if tt == LuaType::Nil {
1062 state.pop_n(2);
1063 } else {
1064 state.remove(-2)?;
1065 }
1066 Ok(tt)
1067}
1068
1069pub fn call_meta(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<bool, LuaError> {
1073 let obj = state.abs_index(obj);
1074 if get_metafield(state, obj, event)? == LuaType::Nil {
1075 return Ok(false);
1076 }
1077 state.push_value(obj)?;
1078 state.call(1, 1)?;
1079 Ok(true)
1080}
1081
1082pub fn lua_len(state: &mut LuaState, idx: i32) -> Result<i64, LuaError> {
1086 state.len_op(idx)?;
1087 let l = match state.to_integer_x(-1) {
1088 Some(n) => n,
1089 None => {
1090 return Err(LuaError::runtime(format_args!(
1091 "object length is not an integer"
1092 )));
1093 }
1094 };
1095 state.pop_n(1);
1096 Ok(l)
1097}
1098
1099pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Vec<u8>, LuaError> {
1103 let idx = state.abs_index(idx);
1104 if call_meta(state, idx, b"__tostring")? {
1105 if state.type_at(-1) != LuaType::String {
1106 return Err(LuaError::runtime(format_args!(
1107 "'__tostring' must return a string"
1108 )));
1109 }
1110 } else {
1111 match state.type_at(idx) {
1112 LuaType::Number => {
1113 if state.is_integer(idx) {
1114 let i = state.to_integer_x(idx).unwrap_or(0);
1115 state.push_fstring(format_args!("{}", i))?;
1116 } else {
1117 let f = state.to_number_x(idx).unwrap_or(0.0);
1118 state.push_fstring(format_args!("{:?}", f))?;
1119 }
1120 }
1121 LuaType::String => {
1122 state.push_value(idx)?;
1123 }
1124 LuaType::Boolean => {
1125 let b = state.to_boolean(idx);
1126 state.push_string(if b { b"true" } else { b"false" })?;
1127 }
1128 LuaType::Nil => {
1129 state.push_string(b"nil")?;
1130 }
1131 _ => {
1132 let tt = get_metafield(state, idx, b"__name")?;
1133 let kind: Vec<u8> = if tt == LuaType::String {
1134 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1135 } else {
1136 state.type_name_at(idx).to_vec()
1137 };
1138 state.push_fstring(format_args!("{}: 0x?", BStr(&kind)))?;
1141 if tt != LuaType::Nil {
1142 state.remove(-2)?;
1143 }
1144 }
1145 }
1146 }
1147 Ok(state.peek_bytes(-1).unwrap_or_default())
1148}
1149
1150pub fn set_funcs(
1154 state: &mut LuaState,
1155 l: &[LuaReg],
1156 nup: i32,
1157) -> Result<(), LuaError> {
1158 check_stack(state, nup, Some(b"too many upvalues"))?;
1159 for reg in l {
1160 match reg.func {
1161 None => {
1162 state.push(LuaValue::Bool(false));
1163 }
1164 Some(f) => {
1165 for _ in 0..nup {
1166 state.push_value(-nup)?;
1167 }
1168 state.push_c_closure(f, nup)?;
1169 }
1170 }
1171 state.set_field(-(nup + 2), reg.name)?;
1172 }
1173 state.pop_n(nup as usize);
1174 Ok(())
1175}
1176
1177pub fn get_subtable(
1181 state: &mut LuaState,
1182 idx: i32,
1183 fname: &[u8],
1184) -> Result<bool, LuaError> {
1185 if state.get_field(idx, fname)? == LuaType::Table {
1186 return Ok(true);
1187 }
1188 state.pop_n(1);
1189 let idx = state.abs_index(idx);
1190 let new_tbl = state.new_table();
1191 state.push(LuaValue::Table(new_tbl));
1192 state.push_value(-1)?;
1193 state.set_field(idx, fname)?;
1194 Ok(false)
1195}
1196
1197pub fn requiref(
1202 state: &mut LuaState,
1203 modname: &[u8],
1204 openf: fn(&mut LuaState) -> Result<usize, LuaError>,
1205 glb: bool,
1206) -> Result<(), LuaError> {
1207 get_subtable(state, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
1208 state.get_field(-1, modname)?;
1209 if !state.to_boolean(-1) {
1210 state.pop_n(1);
1211 state.push_c_function(openf)?;
1212 state.push_bytes(modname)?;
1213 state.call(1, 1)?;
1214 state.push_value(-1)?;
1215 state.set_field(-3, modname)?;
1216 }
1217 state.remove(-2)?;
1218 if glb {
1219 state.push_value(-1)?;
1220 state.set_global(modname)?;
1221 }
1222 Ok(())
1223}
1224
1225pub fn get_metatable(state: &mut LuaState, tname: &[u8]) -> Result<LuaType, LuaError> {
1230 state.get_field(LUA_REGISTRYINDEX, tname)
1231}
1232
1233pub fn new_state() -> Result<LuaState, LuaError> {
1239 let _ = default_panic_handler;
1242 let _ = warn_off;
1243 todo!("phase-b: LuaState::new()")
1244}
1245
1246fn default_panic_handler(state: &mut LuaState) -> Result<usize, LuaError> {
1249 let msg = if state.type_at(-1) == LuaType::String {
1250 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1251 } else {
1252 b"error object is not a string".to_vec()
1253 };
1254 eprintln!("PANIC: unprotected error in call to Lua API ({})", BStr(&msg));
1255 Ok(0) }
1257
1258fn warn_off(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1261 check_control(state, message, tocont)?;
1262 Ok(())
1263}
1264
1265fn warn_on(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1268 if check_control(state, message, tocont)? {
1269 return Ok(());
1270 }
1271 eprint!("Lua warning: ");
1272 warn_cont(state, message, tocont)
1273}
1274
1275fn warn_cont(_state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1278 eprint!("{}", BStr(message));
1279 if tocont {
1281 let _ = (warn_cont as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1282 } else {
1283 eprintln!();
1284 let _ = (warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1285 }
1286 Ok(())
1287}
1288
1289fn check_control(
1293 state: &mut LuaState,
1294 message: &[u8],
1295 tocont: bool,
1296) -> Result<bool, LuaError> {
1297 if tocont || message.first() != Some(&b'@') {
1298 return Ok(false);
1299 }
1300 let cmd = &message[1..];
1301 let _ = state;
1303 if cmd == b"off" {
1304 let _ = warn_off as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1305 } else if cmd == b"on" {
1306 let _ = warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1307 }
1308 Ok(true)
1309}
1310
1311pub fn check_version(state: &mut LuaState, ver: f64, sz: usize) -> Result<(), LuaError> {
1314 const LUAL_NUMSIZES: usize = std::mem::size_of::<i64>() * 16 + std::mem::size_of::<f64>();
1315 if sz != LUAL_NUMSIZES {
1316 return Err(LuaError::runtime(format_args!(
1317 "core and library have incompatible numeric types"
1318 )));
1319 }
1320 let v = state.lua_version();
1321 if (v - ver).abs() > f64::EPSILON {
1322 return Err(LuaError::runtime(format_args!(
1323 "version mismatch: app. needs {}, Lua core provides {}",
1324 ver, v
1325 )));
1326 }
1327 Ok(())
1328}
1329
1330struct BStr<'a>(&'a [u8]);
1338
1339impl<'a> std::fmt::Display for BStr<'a> {
1340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1341 for &b in self.0 {
1342 if b.is_ascii() {
1343 f.write_char(b as char)?;
1344 } else {
1345 write!(f, "\\x{:02x}", b)?;
1346 }
1347 }
1348 Ok(())
1349 }
1350}
1351
1352use std::fmt::Write as _;
1354
1355