1use lua_types::{
25 error::LuaError,
26 value::LuaValue,
27 gc::GcRef,
28 string::LuaString,
29 userdata::LuaUserData,
30 LuaType,
31 LuaStatus,
32
33};
34use crate::state_stub::{LuaState, LuaStateStubExt as _, LuaDebug};
35
36const LEVELS1: i32 = 10;
40
41const LEVELS2: i32 = 11;
43
44const FREELIST_REF: i64 = 3; pub const LUA_REFNIL: i32 = -1;
50
51pub const LUA_NOREF: i32 = -2;
53
54pub const LUA_ERRFILE: i32 = 6;
56
57pub const LUA_LOADED_TABLE: &[u8] = b"_LOADED";
59
60pub const LUA_PRELOAD_TABLE: &[u8] = b"_PRELOAD";
62
63pub const LUA_GNAME: &[u8] = b"_G";
65
66pub const LUA_FILE_HANDLE: &[u8] = b"FILE*";
68
69const LUA_REGISTRYINDEX: i32 = -1_001_000;
71
72#[expect(dead_code, reason = "ported stdlib helper; not yet wired into the runtime")]
74const LUA_MINSTACK: i32 = 20;
75
76pub struct LuaReg {
84 pub name: &'static [u8],
85 pub func: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
86}
87
88pub struct LuaBuffer {
95 pub data: Vec<u8>,
96}
97
98pub struct LuaStream {
105 pub f: Option<Box<dyn std::io::Read>>,
109 pub closef: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
111}
112
113fn find_field(
120 state: &mut LuaState,
121 objidx: i32,
122 level: i32,
123) -> Result<bool, LuaError> {
124 if level == 0 || state.type_at(-1) != LuaType::Table {
125 return Ok(false);
126 }
127 state.push(LuaValue::Nil);
128 while state.table_next(-2)? {
129 if state.type_at(-2) == LuaType::String {
130 if state.raw_equal(objidx, -1)? {
131 state.pop_n(1); return Ok(true);
133 } else if find_field(state, objidx, level - 1)? {
134 state.push_string(b".")?; state.replace(-3)?; state.concat(3)?; return Ok(true);
139 }
140 }
141 state.pop_n(1); }
143 Ok(false)
144}
145
146fn push_global_func_name(
150 state: &mut LuaState,
151 ar: &mut LuaDebug,
152) -> Result<bool, LuaError> {
153 let top = state.top_count();
154 state.get_info(b"f", ar)?;
155 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
156 check_stack(state, 6, Some(b"not enough stack"))?;
157 if find_field(state, top + 1, 2)? {
158 if state.peek_bytes(-1).map_or(false, |n| n.starts_with(b"_G.")) {
159 let suffix = state.peek_bytes(-1)
160 .map(|n| n[3..].to_vec())
161 .unwrap_or_default();
162 state.push_bytes(&suffix)?;
163 state.remove(-2)?;
164 }
165 state.copy_value(-1, top + 1)?;
166 lua_vm::api::set_top(state, top + 1)?;
167 Ok(true)
168 } else {
169 lua_vm::api::set_top(state, top)?;
170 Ok(false)
171 }
172}
173
174fn push_global_func_name_from_target(
175 state: &mut LuaState,
176 target: &mut LuaState,
177 ar: &mut LuaDebug,
178) -> Result<bool, LuaError> {
179 let top = state.top_count();
180 target.get_info(b"f", ar)?;
181 let func = target.get_at(target.top_idx() - 1);
182 target.pop_n(1);
183 state.push(func);
184 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
185 check_stack(state, 6, Some(b"not enough stack"))?;
186 if find_field(state, top + 1, 2)? {
187 if state.peek_bytes(-1).map_or(false, |n| n.starts_with(b"_G.")) {
188 let suffix = state.peek_bytes(-1)
189 .map(|n| n[3..].to_vec())
190 .unwrap_or_default();
191 state.push_bytes(&suffix)?;
192 state.remove(-2)?;
193 }
194 state.copy_value(-1, top + 1)?;
195 lua_vm::api::set_top(state, top + 1)?;
196 Ok(true)
197 } else {
198 lua_vm::api::set_top(state, top)?;
199 Ok(false)
200 }
201}
202
203fn push_func_name(
206 state: &mut LuaState,
207 ar: &mut LuaDebug,
208 global_lookup_target: Option<&mut LuaState>,
209) -> Result<(), LuaError> {
210 let found_global = match global_lookup_target {
211 Some(target) => push_global_func_name_from_target(state, target, ar)?,
212 None => push_global_func_name(state, ar)?,
213 };
214 if found_global {
215 let name = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
216 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
217 state.remove(-2)?;
218 } else if !ar.namewhat.is_empty() {
219 let namewhat = ar.namewhat.clone();
220 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
221 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
222 } else if ar.what == b'm' {
223 state.push_string(b"main chunk")?;
224 } else if ar.what != b'C' {
225 let src = ar.short_src.clone();
226 let line = ar.linedefined;
227 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
228 } else {
229 state.push_string(b"?")?;
230 }
231 Ok(())
232}
233
234fn last_level(state: &mut LuaState) -> i32 {
237 let mut ar = LuaDebug::default();
238 let mut li: i32 = 1;
239 let mut le: i32 = 1;
240 while state.get_stack(le, &mut ar) {
241 li = le;
242 le *= 2;
243 }
244 while li < le {
246 let m = (li + le) / 2;
247 if state.get_stack(m, &mut ar) {
248 li = m + 1;
249 } else {
250 le = m;
251 }
252 }
253 le - 1
254}
255
256pub fn traceback(
266 state: &mut LuaState,
267 mut other: Option<&mut LuaState>,
268 msg: Option<&[u8]>,
269 level: i32,
270) -> Result<(), LuaError> {
271 let mut b = LuaBuffer::new();
272 let mut ar = LuaDebug::default();
273 let last = match &mut other {
274 Some(o) => last_level(o),
275 None => last_level(state),
276 };
277 let mut limit2show: i32 = if last - level > LEVELS1 + LEVELS2 { LEVELS1 } else { -1 };
278 buf_init(state, &mut b);
279 if let Some(m) = msg {
280 add_lstring(&mut b, m);
281 add_char(&mut b, b'\n');
282 }
283 add_lstring(&mut b, b"stack traceback:");
284 let mut level = level;
285 loop {
286 let got = match &mut other {
287 Some(o) => o.get_stack(level, &mut ar),
288 None => state.get_stack(level, &mut ar),
289 };
290 if !got {
291 break;
292 }
293 level += 1;
294 if limit2show == 0 {
295 let n = last - level - LEVELS2 + 1;
296 state.push_fstring(format_args!("\n\t...\t(skipping {} levels)", n))?;
297 add_value(state, &mut b)?;
298 level += n;
299 limit2show = LEVELS2;
300 } else {
301 limit2show -= 1;
302 match &mut other {
303 Some(o) => o.get_info(b"Slnt", &mut ar)?,
304 None => state.get_info(b"Slnt", &mut ar)?,
305 }
306 if ar.currentline <= 0 {
307 let src = ar.short_src.clone();
308 state.push_fstring(format_args!("\n\t{}: in ", BStr(&src)))?;
309 } else {
310 let src = ar.short_src.clone();
311 let line = ar.currentline;
312 state.push_fstring(format_args!("\n\t{}:{}: in ", BStr(&src), line))?;
313 }
314 add_value(state, &mut b)?;
315 match &mut other {
316 Some(o) => push_func_name(state, &mut ar, Some(&mut **o))?,
317 None => push_func_name(state, &mut ar, None)?,
318 }
319 add_value(state, &mut b)?;
320 if ar.istailcall {
321 add_lstring(&mut b, b"\n\t(...tail calls...)");
322 }
323 }
324 }
325 push_result(state, &mut b)?;
326 Ok(())
327}
328
329pub fn arg_error(
336 state: &mut LuaState,
337 mut arg: i32,
338 extramsg: &[u8],
339) -> Result<usize, LuaError> {
340 let mut ar = LuaDebug::default();
341 if !state.get_stack(0, &mut ar) {
342 return Err(LuaError::runtime(format_args!(
343 "bad argument #{} ({})",
344 arg,
345 BStr(extramsg)
346 )));
347 }
348 state.get_info(b"n", &mut ar)?;
349 if ar.namewhat == b"method" {
350 arg -= 1; if arg == 0 {
352 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
353 return Err(LuaError::runtime(format_args!(
354 "calling '{}' on bad self ({})",
355 BStr(&name),
356 BStr(extramsg)
357 )));
358 }
359 }
360 let fname = if ar.name.is_none() {
361 if push_global_func_name(state, &mut ar)? {
362 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
363 } else {
364 b"?".to_vec()
365 }
366 } else {
367 ar.name.clone().unwrap_or_else(|| b"?".to_vec())
368 };
369 Err(LuaError::runtime(format_args!(
370 "bad argument #{} to '{}' ({})",
371 arg,
372 BStr(&fname),
373 BStr(extramsg)
374 )))
375}
376
377pub fn type_error_arg(
381 state: &mut LuaState,
382 arg: i32,
383 tname: &[u8],
384) -> Result<usize, LuaError> {
385 let typearg: Vec<u8> = if get_metafield(state, arg, b"__name")? == LuaType::String {
391 let bytes = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
392 state.pop_n(1);
393 bytes
394 } else if state.type_at(arg) == LuaType::LightUserData {
395 b"light userdata".to_vec()
396 } else {
397 state.type_name_at(arg).to_vec()
398 };
399 let msg_owned = format!(
400 "{} expected, got {}",
401 BStr(tname),
402 BStr(&typearg)
403 );
404 arg_error(state, arg, msg_owned.as_bytes())
405}
406
407fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
410 let name = state.type_name(tag);
411 type_error_arg(state, arg, name)?;
412 Ok(())
413}
414
415pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
419 let mut ar = LuaDebug::default();
420 if state.get_stack(level, &mut ar) {
421 state.get_info(b"Sl", &mut ar)?;
422 if ar.currentline > 0 {
423 let src = ar.short_src.clone();
424 let line = ar.currentline;
425 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
426 return Ok(());
427 }
428 }
429 state.push_string(b"")?;
430 Ok(())
431}
432
433pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
440 push_where(state, 1)?;
441 let where_str = state.pop_bytes();
442 let full = [where_str.as_slice(), msg].concat();
443 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
444}
445
446pub fn file_result(
451 state: &mut LuaState,
452 stat: bool,
453 fname: Option<&[u8]>,
454) -> Result<usize, LuaError> {
455 if stat {
456 state.push(LuaValue::Bool(true));
457 Ok(1)
458 } else {
459 state.push(LuaValue::Nil);
460 let errmsg = b"(errno unavailable in Rust port)".to_vec();
462 if let Some(name) = fname {
463 let full = [name, b": ".as_slice(), &errmsg].concat();
464 state.push_bytes(&full)?;
465 } else {
466 state.push_bytes(&errmsg)?;
467 }
468 state.push(LuaValue::Int(0));
470 Ok(3)
471 }
472}
473
474pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
479 if stat != 0 {
480 return file_result(state, false, None);
481 }
482 let what = b"exit".as_slice();
483 state.push(LuaValue::Bool(true));
484 state.push_bytes(what)?;
485 state.push(LuaValue::Int(stat as i64));
486 Ok(3)
487}
488
489pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
496 if get_metatable(state, tname)? != LuaType::Nil {
497 return Ok(false); }
499 state.pop_n(1);
500 state.create_table(0, 2)?;
501 state.push_bytes(tname)?;
502 state.set_field(-2, b"__name")?;
503 state.push_value(-1)?;
504 state.set_field(LUA_REGISTRYINDEX, tname)?;
505 Ok(true)
506}
507
508pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
511 get_metatable(state, tname)?;
512 state.set_metatable(-2)?;
513 Ok(())
514}
515
516pub fn test_udata(
520 state: &mut LuaState,
521 ud: i32,
522 tname: &[u8],
523) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
524 let p = state.to_userdata(ud);
525 if let Some(p) = p {
526 if state.get_metatable(ud)? {
527 get_metatable(state, tname)?;
528 let eq = state.raw_equal(-1, -2)?;
529 state.pop_n(2); if eq {
531 return Ok(Some(p));
532 }
533 }
534 }
535 Ok(None)
536}
537
538pub fn check_udata(
541 state: &mut LuaState,
542 ud: i32,
543 tname: &[u8],
544) -> Result<GcRef<LuaUserData>, LuaError> {
545 match test_udata(state, ud, tname)? {
546 Some(p) => Ok(p),
547 None => {
548 type_error_arg(state, ud, tname)?;
549 unreachable!()
550 }
551 }
552}
553
554pub fn check_option(
560 state: &mut LuaState,
561 arg: i32,
562 def: Option<&[u8]>,
563 lst: &[&[u8]],
564) -> Result<usize, LuaError> {
565 let name: Vec<u8> = match def {
566 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
567 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
568 };
569 for (i, entry) in lst.iter().enumerate() {
570 if *entry == name.as_slice() {
571 return Ok(i);
572 }
573 }
574 Err(LuaError::runtime(format_args!(
575 "invalid option '{}'",
576 BStr(&name)
577 )))
578}
579
580pub fn check_stack(
583 state: &mut LuaState,
584 space: i32,
585 msg: Option<&[u8]>,
586) -> Result<(), LuaError> {
587 if !state.check_stack_space(space) {
588 match msg {
589 Some(m) => {
590 return Err(LuaError::runtime(format_args!(
591 "stack overflow ({})",
592 BStr(m)
593 )));
594 }
595 None => {
596 return Err(LuaError::runtime(format_args!("stack overflow")));
597 }
598 }
599 }
600 Ok(())
601}
602
603pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
606 if state.type_at(arg) != t {
607 tag_error(state, arg, t)?;
608 }
609 Ok(())
610}
611
612pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
615 if state.type_at(arg) == LuaType::None {
616 return Err(LuaError::arg_error(arg, "value expected"));
617 }
618 Ok(())
619}
620
621pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
624 match state.to_lua_string(arg) {
625 Some(s) => Ok(s),
626 None => {
627 tag_error(state, arg, LuaType::String)?;
628 unreachable!()
629 }
630 }
631}
632
633pub fn opt_lstring(
636 state: &mut LuaState,
637 arg: i32,
638 def: Option<&[u8]>,
639) -> Result<Option<Vec<u8>>, LuaError> {
640 if state.is_none_or_nil(arg) {
641 return Ok(def.map(|d| d.to_vec()));
642 }
643 let s = check_lstring(state, arg)?;
644 Ok(Some(s.as_bytes().to_vec()))
645}
646
647pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
650 match state.to_number_x(arg) {
651 Some(d) => Ok(d),
652 None => {
653 tag_error(state, arg, LuaType::Number)?;
654 unreachable!()
655 }
656 }
657}
658
659pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
662 if state.is_none_or_nil(arg) {
663 Ok(def)
664 } else {
665 check_number(state, arg)
666 }
667}
668
669fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
675 if state.is_number(arg) {
676 Err(LuaError::arg_error(
677 arg,
678 "number has no integer representation",
679 ))
680 } else {
681 tag_error(state, arg, LuaType::Number)?;
682 unreachable!("tag_error always returns Err")
683 }
684}
685
686pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
689 match state.to_integer_x(arg) {
690 Some(d) => Ok(d),
691 None => {
692 int_error(state, arg)?;
693 unreachable!("int_error always returns Err")
694 }
695 }
696}
697
698pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
701 if state.is_none_or_nil(arg) {
702 Ok(def)
703 } else {
704 check_integer(state, arg)
705 }
706}
707
708impl LuaBuffer {
711 pub fn new() -> Self {
715 LuaBuffer { data: Vec::new() }
716 }
717
718 pub fn len(&self) -> usize {
720 self.data.len()
721 }
722}
723
724impl Default for LuaBuffer {
725 fn default() -> Self {
726 LuaBuffer::new()
727 }
728}
729
730pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
736 *buf = LuaBuffer::new();
740 let _ = state.push(LuaValue::Nil);
742}
743
744pub fn buf_init_size(
747 state: &mut LuaState,
748 buf: &mut LuaBuffer,
749 sz: usize,
750) -> Result<(), LuaError> {
751 buf_init(state, buf);
752 buf.data.reserve(sz);
753 Ok(())
754}
755
756fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
760 if usize::MAX - sz < buf.len() {
761 return Err(LuaError::runtime(format_args!("buffer too large")));
762 }
763 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
765 Ok(buf.len() + sz)
766 } else {
767 Ok(newsize)
768 }
769}
770
771pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
774 if buf.data.capacity() - buf.data.len() < sz {
775 let newcap = new_buff_size(buf, sz)?;
776 buf.data.reserve(newcap - buf.data.len());
777 }
778 Ok(())
779}
780
781pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
784 if !s.is_empty() {
785 buf.data.extend_from_slice(s);
786 }
787}
788
789pub fn add_char(buf: &mut LuaBuffer, c: u8) {
792 buf.data.push(c);
793}
794
795pub fn add_size(_buf: &mut LuaBuffer, sz: usize) {
798 let _ = sz;
803}
804
805pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
808 if let Some(bytes) = state.peek_bytes(-1) {
809 let owned = bytes.to_vec();
810 add_lstring(buf, &owned);
811 }
812 state.pop_n(1);
813 Ok(())
814}
815
816pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
819 state.push_bytes(&buf.data)?;
820 state.remove(-2)?;
821 Ok(())
822}
823
824pub fn push_result_size(
827 state: &mut LuaState,
828 buf: &mut LuaBuffer,
829 sz: usize,
830) -> Result<(), LuaError> {
831 add_size(buf, sz);
832 push_result(state, buf)
833}
834
835pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
839 if pat.is_empty() {
840 add_lstring(buf, s);
841 return;
842 }
843 let mut remaining = s;
844 while let Some(pos) = find_bytes(remaining, pat) {
845 add_lstring(buf, &remaining[..pos]);
846 add_lstring(buf, repl);
847 remaining = &remaining[pos + pat.len()..];
848 }
849 add_lstring(buf, remaining);
850}
851
852pub fn gsub<'a>(
856 state: &'a mut LuaState,
857 s: &[u8],
858 pat: &[u8],
859 repl: &[u8],
860) -> Result<Vec<u8>, LuaError> {
861 let mut b = LuaBuffer::new();
862 buf_init(state, &mut b);
863 add_gsub(&mut b, s, pat, repl);
864 push_result(state, &mut b)?;
865 Ok(state.peek_bytes(-1).unwrap_or_default())
866}
867
868fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
872 if needle.is_empty() {
873 return Some(0);
874 }
875 haystack.windows(needle.len()).position(|w| w == needle)
876}
877
878pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
885 if state.type_at(-1) == LuaType::Nil {
886 state.pop_n(1);
887 return Ok(LUA_REFNIL);
888 }
889 let t = state.abs_index(t);
890 let ref_val: i32;
891 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
892 ref_val = 0; state.push(LuaValue::Int(0));
894 state.raw_set_i(t, FREELIST_REF)?;
895 } else {
896 debug_assert!(state.type_at(-1) == LuaType::Number);
897 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
898 }
899 state.pop_n(1); let next_ref: i32;
901 if ref_val != 0 {
902 state.raw_get_i(t, ref_val as i64)?;
903 state.raw_set_i(t, FREELIST_REF)?;
904 next_ref = ref_val;
905 } else {
906 next_ref = (state.raw_len(t) as i32) + 1;
907 }
908 state.raw_set_i(t, next_ref as i64)?;
909 Ok(next_ref)
910}
911
912pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
915 if r >= 0 {
916 let t = state.abs_index(t);
917 state.raw_get_i(t, FREELIST_REF)?;
918 debug_assert!(state.type_at(-1) == LuaType::Number);
919 state.raw_set_i(t, r as i64)?;
920 state.push(LuaValue::Int(r as i64));
921 state.raw_set_i(t, FREELIST_REF)?;
922 }
923 Ok(())
924}
925
926fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
931 let mut remaining = Some(data);
932 move || remaining.take()
933}
934
935fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
944 let s = if buf.starts_with(b"\xEF\xBB\xBF") { &buf[3..] } else { buf };
945 if s.first() == Some(&b'#') {
946 let nl = s.iter().position(|&b| b == b'\n').map(|p| p + 1).unwrap_or(s.len());
947 let rest = &s[nl..];
948 if rest.first() == Some(&0x1B) {
949 rest.to_vec()
950 } else {
951 let mut out = Vec::with_capacity(rest.len() + 1);
952 out.push(b'\n');
953 out.extend_from_slice(rest);
954 out
955 }
956 } else {
957 s.to_vec()
958 }
959}
960
961pub fn load_filex(
971 state: &mut LuaState,
972 filename: Option<&[u8]>,
973 mode: Option<&[u8]>,
974) -> Result<i32, LuaError> {
975 let _ = mode;
976 let fname = match filename {
977 Some(f) => f,
978 None => {
979 state.push_string(b"cannot read stdin: no filename given")?;
982 return Ok(LUA_ERRFILE);
983 }
984 };
985 let path = match std::str::from_utf8(fname) {
986 Ok(s) => std::path::PathBuf::from(s),
987 Err(_) => {
988 state.push_fstring(format_args!(
989 "cannot open {}: invalid utf-8 in filename",
990 BStr(fname)
991 ))?;
992 return Ok(LUA_ERRFILE);
993 }
994 };
995 let raw = match std::fs::read(&path) {
996 Ok(bytes) => bytes,
997 Err(e) => {
998 state.push_fstring(format_args!(
999 "cannot open {}: {}",
1000 BStr(fname),
1001 e
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