1use lua_types::{
25 error::LuaError,
26 value::LuaValue,
27 gc::GcRef,
28 string::LuaString,
29 userdata::LuaUserData,
30 LuaType,
31 LuaStatus,
32 arith::ArithOp,
33};
34use crate::state_stub::{LuaState, LuaStateStubExt as _, lua_CFunction, upvalue_index, CompareOp, 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
72const LUA_MINSTACK: i32 = 20;
74
75pub struct LuaReg {
83 pub name: &'static [u8],
84 pub func: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
85}
86
87pub struct LuaBuffer {
94 pub data: Vec<u8>,
95}
96
97pub struct LuaStream {
104 pub f: Option<Box<dyn std::io::Read>>,
108 pub closef: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
110}
111
112fn find_field(
119 state: &mut LuaState,
120 objidx: i32,
121 level: i32,
122) -> Result<bool, LuaError> {
123 if level == 0 || state.type_at(-1) != LuaType::Table {
124 return Ok(false);
125 }
126 state.push(LuaValue::Nil);
127 while state.table_next(-2)? {
128 if state.type_at(-2) == LuaType::String {
129 if state.raw_equal(objidx, -1)? {
130 state.pop_n(1); return Ok(true);
132 } else if find_field(state, objidx, level - 1)? {
133 state.push_string(b".")?; state.replace(-3); state.concat(3)?; return Ok(true);
138 }
139 }
140 state.pop_n(1); }
142 Ok(false)
143}
144
145fn push_global_func_name(
149 state: &mut LuaState,
150 ar: &mut LuaDebug,
151) -> Result<bool, LuaError> {
152 let top = state.top_count();
153 state.get_info(b"f", ar)?;
154 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
155 check_stack(state, 6, Some(b"not enough stack"))?;
156 if find_field(state, top + 1, 2)? {
157 if state.peek_bytes(-1).map_or(false, |n| n.starts_with(b"_G.")) {
158 let suffix = state.peek_bytes(-1)
159 .map(|n| n[3..].to_vec())
160 .unwrap_or_default();
161 state.push_bytes(&suffix)?;
162 state.remove(-2)?;
163 }
164 state.copy_value(-1, top + 1)?;
165 lua_vm::api::set_top(state, top + 1)?;
166 Ok(true)
167 } else {
168 lua_vm::api::set_top(state, top)?;
169 Ok(false)
170 }
171}
172
173fn push_global_func_name_from_target(
174 state: &mut LuaState,
175 target: &mut LuaState,
176 ar: &mut LuaDebug,
177) -> Result<bool, LuaError> {
178 let top = state.top_count();
179 target.get_info(b"f", ar)?;
180 let func = target.get_at(target.top_idx() - 1);
181 target.pop_n(1);
182 state.push(func);
183 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
184 check_stack(state, 6, Some(b"not enough stack"))?;
185 if find_field(state, top + 1, 2)? {
186 if state.peek_bytes(-1).map_or(false, |n| n.starts_with(b"_G.")) {
187 let suffix = state.peek_bytes(-1)
188 .map(|n| n[3..].to_vec())
189 .unwrap_or_default();
190 state.push_bytes(&suffix)?;
191 state.remove(-2)?;
192 }
193 state.copy_value(-1, top + 1)?;
194 lua_vm::api::set_top(state, top + 1)?;
195 Ok(true)
196 } else {
197 lua_vm::api::set_top(state, top)?;
198 Ok(false)
199 }
200}
201
202fn push_func_name(
205 state: &mut LuaState,
206 ar: &mut LuaDebug,
207 global_lookup_target: Option<&mut LuaState>,
208) -> Result<(), LuaError> {
209 let found_global = match global_lookup_target {
210 Some(target) => push_global_func_name_from_target(state, target, ar)?,
211 None => push_global_func_name(state, ar)?,
212 };
213 if found_global {
214 let name = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
215 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
216 state.remove(-2)?;
217 } else if !ar.namewhat.is_empty() {
218 let namewhat = ar.namewhat.clone();
219 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
220 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
221 } else if ar.what == b'm' {
222 state.push_string(b"main chunk")?;
223 } else if ar.what != b'C' {
224 let src = ar.short_src.clone();
225 let line = ar.linedefined;
226 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
227 } else {
228 state.push_string(b"?")?;
229 }
230 Ok(())
231}
232
233fn last_level(state: &mut LuaState) -> i32 {
236 let mut ar = LuaDebug::default();
237 let mut li: i32 = 1;
238 let mut le: i32 = 1;
239 while state.get_stack(le, &mut ar) {
240 li = le;
241 le *= 2;
242 }
243 while li < le {
245 let m = (li + le) / 2;
246 if state.get_stack(m, &mut ar) {
247 li = m + 1;
248 } else {
249 le = m;
250 }
251 }
252 le - 1
253}
254
255pub fn traceback(
265 state: &mut LuaState,
266 mut other: Option<&mut LuaState>,
267 msg: Option<&[u8]>,
268 level: i32,
269) -> Result<(), LuaError> {
270 let mut b = LuaBuffer::new();
271 let mut ar = LuaDebug::default();
272 let last = match &mut other {
273 Some(o) => last_level(o),
274 None => last_level(state),
275 };
276 let mut limit2show: i32 = if last - level > LEVELS1 + LEVELS2 { LEVELS1 } else { -1 };
277 buf_init(state, &mut b);
278 if let Some(m) = msg {
279 add_lstring(&mut b, m);
280 add_char(&mut b, b'\n');
281 }
282 add_lstring(&mut b, b"stack traceback:");
283 let mut level = level;
284 loop {
285 let got = match &mut other {
286 Some(o) => o.get_stack(level, &mut ar),
287 None => state.get_stack(level, &mut ar),
288 };
289 if !got {
290 break;
291 }
292 level += 1;
293 if limit2show == 0 {
294 let n = last - level - LEVELS2 + 1;
295 state.push_fstring(format_args!("\n\t...\t(skipping {} levels)", n))?;
296 add_value(state, &mut b)?;
297 level += n;
298 limit2show = LEVELS2;
299 } else {
300 limit2show -= 1;
301 match &mut other {
302 Some(o) => o.get_info(b"Slnt", &mut ar)?,
303 None => state.get_info(b"Slnt", &mut ar)?,
304 }
305 if ar.currentline <= 0 {
306 let src = ar.short_src.clone();
307 state.push_fstring(format_args!("\n\t{}: in ", BStr(&src)))?;
308 } else {
309 let src = ar.short_src.clone();
310 let line = ar.currentline;
311 state.push_fstring(format_args!("\n\t{}:{}: in ", BStr(&src), line))?;
312 }
313 add_value(state, &mut b)?;
314 match &mut other {
315 Some(o) => push_func_name(state, &mut ar, Some(&mut **o))?,
316 None => push_func_name(state, &mut ar, None)?,
317 }
318 add_value(state, &mut b)?;
319 if ar.istailcall {
320 add_lstring(&mut b, b"\n\t(...tail calls...)");
321 }
322 }
323 }
324 push_result(state, &mut b)?;
325 Ok(())
326}
327
328pub fn arg_error(
335 state: &mut LuaState,
336 mut arg: i32,
337 extramsg: &[u8],
338) -> Result<usize, LuaError> {
339 let mut ar = LuaDebug::default();
340 if !state.get_stack(0, &mut ar) {
341 return Err(LuaError::runtime(format_args!(
342 "bad argument #{} ({})",
343 arg,
344 BStr(extramsg)
345 )));
346 }
347 state.get_info(b"n", &mut ar)?;
348 if ar.namewhat == b"method" {
349 arg -= 1; if arg == 0 {
351 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
352 return Err(LuaError::runtime(format_args!(
353 "calling '{}' on bad self ({})",
354 BStr(&name),
355 BStr(extramsg)
356 )));
357 }
358 }
359 let fname = if ar.name.is_none() {
360 if push_global_func_name(state, &mut ar)? {
361 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
362 } else {
363 b"?".to_vec()
364 }
365 } else {
366 ar.name.clone().unwrap_or_else(|| b"?".to_vec())
367 };
368 Err(LuaError::runtime(format_args!(
369 "bad argument #{} to '{}' ({})",
370 arg,
371 BStr(&fname),
372 BStr(extramsg)
373 )))
374}
375
376pub fn type_error_arg(
380 state: &mut LuaState,
381 arg: i32,
382 tname: &[u8],
383) -> Result<usize, LuaError> {
384 let typearg: Vec<u8> = if get_metafield(state, arg, b"__name")? == LuaType::String {
390 let bytes = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
391 state.pop_n(1);
392 bytes
393 } else if state.type_at(arg) == LuaType::LightUserData {
394 b"light userdata".to_vec()
395 } else {
396 state.type_name_at(arg).to_vec()
397 };
398 let msg_owned = format!(
399 "{} expected, got {}",
400 BStr(tname),
401 BStr(&typearg)
402 );
403 arg_error(state, arg, msg_owned.as_bytes())
404}
405
406fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
409 let name = state.type_name(tag);
410 type_error_arg(state, arg, name)?;
411 Ok(())
412}
413
414pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
418 let mut ar = LuaDebug::default();
419 if state.get_stack(level, &mut ar) {
420 state.get_info(b"Sl", &mut ar)?;
421 if ar.currentline > 0 {
422 let src = ar.short_src.clone();
423 let line = ar.currentline;
424 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
425 return Ok(());
426 }
427 }
428 state.push_string(b"")?;
429 Ok(())
430}
431
432pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
439 push_where(state, 1)?;
440 let where_str = state.pop_bytes();
441 let full = [where_str.as_slice(), msg].concat();
442 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
443}
444
445pub fn file_result(
450 state: &mut LuaState,
451 stat: bool,
452 fname: Option<&[u8]>,
453) -> Result<usize, LuaError> {
454 if stat {
455 state.push(LuaValue::Bool(true));
456 Ok(1)
457 } else {
458 state.push(LuaValue::Nil);
459 let errmsg = b"(errno unavailable in Rust port)".to_vec();
461 if let Some(name) = fname {
462 let full = [name, b": ".as_slice(), &errmsg].concat();
463 state.push_bytes(&full)?;
464 } else {
465 state.push_bytes(&errmsg)?;
466 }
467 state.push(LuaValue::Int(0));
469 Ok(3)
470 }
471}
472
473pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
478 if stat != 0 {
479 return file_result(state, false, None);
480 }
481 let what = b"exit".as_slice();
482 state.push(LuaValue::Bool(true));
483 state.push_bytes(what)?;
484 state.push(LuaValue::Int(stat as i64));
485 Ok(3)
486}
487
488pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
495 if get_metatable(state, tname)? != LuaType::Nil {
496 return Ok(false); }
498 state.pop_n(1);
499 state.create_table(0, 2)?;
500 state.push_bytes(tname)?;
501 state.set_field(-2, b"__name")?;
502 state.push_value(-1)?;
503 state.set_field(LUA_REGISTRYINDEX, tname)?;
504 Ok(true)
505}
506
507pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
510 get_metatable(state, tname)?;
511 state.set_metatable(-2)?;
512 Ok(())
513}
514
515pub fn test_udata(
519 state: &mut LuaState,
520 ud: i32,
521 tname: &[u8],
522) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
523 let p = state.to_userdata(ud);
524 if let Some(p) = p {
525 if state.get_metatable(ud)? {
526 get_metatable(state, tname)?;
527 let eq = state.raw_equal(-1, -2)?;
528 state.pop_n(2); if eq {
530 return Ok(Some(p));
531 }
532 }
533 }
534 Ok(None)
535}
536
537pub fn check_udata(
540 state: &mut LuaState,
541 ud: i32,
542 tname: &[u8],
543) -> Result<GcRef<LuaUserData>, LuaError> {
544 match test_udata(state, ud, tname)? {
545 Some(p) => Ok(p),
546 None => {
547 type_error_arg(state, ud, tname)?;
548 unreachable!()
549 }
550 }
551}
552
553pub fn check_option(
559 state: &mut LuaState,
560 arg: i32,
561 def: Option<&[u8]>,
562 lst: &[&[u8]],
563) -> Result<usize, LuaError> {
564 let name: Vec<u8> = match def {
565 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
566 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
567 };
568 for (i, entry) in lst.iter().enumerate() {
569 if *entry == name.as_slice() {
570 return Ok(i);
571 }
572 }
573 Err(LuaError::runtime(format_args!(
574 "invalid option '{}'",
575 BStr(&name)
576 )))
577}
578
579pub fn check_stack(
582 state: &mut LuaState,
583 space: i32,
584 msg: Option<&[u8]>,
585) -> Result<(), LuaError> {
586 if !state.check_stack_space(space) {
587 match msg {
588 Some(m) => {
589 return Err(LuaError::runtime(format_args!(
590 "stack overflow ({})",
591 BStr(m)
592 )));
593 }
594 None => {
595 return Err(LuaError::runtime(format_args!("stack overflow")));
596 }
597 }
598 }
599 Ok(())
600}
601
602pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
605 if state.type_at(arg) != t {
606 tag_error(state, arg, t)?;
607 }
608 Ok(())
609}
610
611pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
614 if state.type_at(arg) == LuaType::None {
615 return Err(LuaError::arg_error(arg, "value expected"));
616 }
617 Ok(())
618}
619
620pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
623 match state.to_lua_string(arg) {
624 Some(s) => Ok(s),
625 None => {
626 tag_error(state, arg, LuaType::String)?;
627 unreachable!()
628 }
629 }
630}
631
632pub fn opt_lstring(
635 state: &mut LuaState,
636 arg: i32,
637 def: Option<&[u8]>,
638) -> Result<Option<Vec<u8>>, LuaError> {
639 if state.is_none_or_nil(arg) {
640 return Ok(def.map(|d| d.to_vec()));
641 }
642 let s = check_lstring(state, arg)?;
643 Ok(Some(s.as_bytes().to_vec()))
644}
645
646pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
649 match state.to_number_x(arg) {
650 Some(d) => Ok(d),
651 None => {
652 tag_error(state, arg, LuaType::Number)?;
653 unreachable!()
654 }
655 }
656}
657
658pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
661 if state.is_none_or_nil(arg) {
662 Ok(def)
663 } else {
664 check_number(state, arg)
665 }
666}
667
668fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
674 if state.is_number(arg) {
675 Err(LuaError::arg_error(
676 arg,
677 "number has no integer representation",
678 ))
679 } else {
680 tag_error(state, arg, LuaType::Number)?;
681 unreachable!("tag_error always returns Err")
682 }
683}
684
685pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
688 match state.to_integer_x(arg) {
689 Some(d) => Ok(d),
690 None => {
691 int_error(state, arg)?;
692 unreachable!("int_error always returns Err")
693 }
694 }
695}
696
697pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
700 if state.is_none_or_nil(arg) {
701 Ok(def)
702 } else {
703 check_integer(state, arg)
704 }
705}
706
707impl LuaBuffer {
710 pub fn new() -> Self {
714 LuaBuffer { data: Vec::new() }
715 }
716
717 pub fn len(&self) -> usize {
719 self.data.len()
720 }
721}
722
723impl Default for LuaBuffer {
724 fn default() -> Self {
725 LuaBuffer::new()
726 }
727}
728
729pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
735 *buf = LuaBuffer::new();
739 let _ = state.push(LuaValue::Nil);
741}
742
743pub fn buf_init_size(
746 state: &mut LuaState,
747 buf: &mut LuaBuffer,
748 sz: usize,
749) -> Result<(), LuaError> {
750 buf_init(state, buf);
751 buf.data.reserve(sz);
752 Ok(())
753}
754
755fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
759 if usize::MAX - sz < buf.len() {
760 return Err(LuaError::runtime(format_args!("buffer too large")));
761 }
762 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
764 Ok(buf.len() + sz)
765 } else {
766 Ok(newsize)
767 }
768}
769
770pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
773 if buf.data.capacity() - buf.data.len() < sz {
774 let newcap = new_buff_size(buf, sz)?;
775 buf.data.reserve(newcap - buf.data.len());
776 }
777 Ok(())
778}
779
780pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
783 if !s.is_empty() {
784 buf.data.extend_from_slice(s);
785 }
786}
787
788pub fn add_char(buf: &mut LuaBuffer, c: u8) {
791 buf.data.push(c);
792}
793
794pub fn add_size(buf: &mut LuaBuffer, sz: usize) {
797 let _ = sz;
802}
803
804pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
807 if let Some(bytes) = state.peek_bytes(-1) {
808 let owned = bytes.to_vec();
809 add_lstring(buf, &owned);
810 }
811 state.pop_n(1);
812 Ok(())
813}
814
815pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
818 state.push_bytes(&buf.data)?;
819 state.remove(-2)?;
820 Ok(())
821}
822
823pub fn push_result_size(
826 state: &mut LuaState,
827 buf: &mut LuaBuffer,
828 sz: usize,
829) -> Result<(), LuaError> {
830 add_size(buf, sz);
831 push_result(state, buf)
832}
833
834pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
838 if pat.is_empty() {
839 add_lstring(buf, s);
840 return;
841 }
842 let mut remaining = s;
843 while let Some(pos) = find_bytes(remaining, pat) {
844 add_lstring(buf, &remaining[..pos]);
845 add_lstring(buf, repl);
846 remaining = &remaining[pos + pat.len()..];
847 }
848 add_lstring(buf, remaining);
849}
850
851pub fn gsub<'a>(
855 state: &'a mut LuaState,
856 s: &[u8],
857 pat: &[u8],
858 repl: &[u8],
859) -> Result<Vec<u8>, LuaError> {
860 let mut b = LuaBuffer::new();
861 buf_init(state, &mut b);
862 add_gsub(&mut b, s, pat, repl);
863 push_result(state, &mut b)?;
864 Ok(state.peek_bytes(-1).unwrap_or_default())
865}
866
867fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
871 if needle.is_empty() {
872 return Some(0);
873 }
874 haystack.windows(needle.len()).position(|w| w == needle)
875}
876
877pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
884 if state.type_at(-1) == LuaType::Nil {
885 state.pop_n(1);
886 return Ok(LUA_REFNIL);
887 }
888 let t = state.abs_index(t);
889 let ref_val: i32;
890 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
891 ref_val = 0; state.push(LuaValue::Int(0));
893 state.raw_set_i(t, FREELIST_REF)?;
894 } else {
895 debug_assert!(state.type_at(-1) == LuaType::Number);
896 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
897 }
898 state.pop_n(1); let next_ref: i32;
900 if ref_val != 0 {
901 state.raw_get_i(t, ref_val as i64)?;
902 state.raw_set_i(t, FREELIST_REF)?;
903 next_ref = ref_val;
904 } else {
905 next_ref = (state.raw_len(t) as i32) + 1;
906 }
907 state.raw_set_i(t, next_ref as i64)?;
908 Ok(next_ref)
909}
910
911pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
914 if r >= 0 {
915 let t = state.abs_index(t);
916 state.raw_get_i(t, FREELIST_REF)?;
917 debug_assert!(state.type_at(-1) == LuaType::Number);
918 state.raw_set_i(t, r as i64)?;
919 state.push(LuaValue::Int(r as i64));
920 state.raw_set_i(t, FREELIST_REF)?;
921 }
922 Ok(())
923}
924
925fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
930 let mut remaining = Some(data);
931 move || remaining.take()
932}
933
934fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
943 let s = if buf.starts_with(b"\xEF\xBB\xBF") { &buf[3..] } else { buf };
944 if s.first() == Some(&b'#') {
945 let nl = s.iter().position(|&b| b == b'\n').map(|p| p + 1).unwrap_or(s.len());
946 let rest = &s[nl..];
947 if rest.first() == Some(&0x1B) {
948 rest.to_vec()
949 } else {
950 let mut out = Vec::with_capacity(rest.len() + 1);
951 out.push(b'\n');
952 out.extend_from_slice(rest);
953 out
954 }
955 } else {
956 s.to_vec()
957 }
958}
959
960pub fn load_filex(
970 state: &mut LuaState,
971 filename: Option<&[u8]>,
972 mode: Option<&[u8]>,
973) -> Result<i32, LuaError> {
974 let _ = mode;
975 let fname = match filename {
976 Some(f) => f,
977 None => {
978 state.push_string(b"cannot read stdin: no filename given")?;
981 return Ok(LUA_ERRFILE);
982 }
983 };
984 let path = match std::str::from_utf8(fname) {
985 Ok(s) => std::path::PathBuf::from(s),
986 Err(_) => {
987 state.push_fstring(format_args!(
988 "cannot open {}: invalid utf-8 in filename",
989 BStr(fname)
990 ))?;
991 return Ok(LUA_ERRFILE);
992 }
993 };
994 let raw = match std::fs::read(&path) {
995 Ok(bytes) => bytes,
996 Err(e) => {
997 state.push_fstring(format_args!(
998 "cannot open {}: {}",
999 BStr(fname),
1000 e
1001 ))?;
1002 return Ok(LUA_ERRFILE);
1003 }
1004 };
1005 let payload = skip_bom_and_shebang(&raw);
1006 let mut once = Some(payload);
1007 let boxed: Box<dyn FnMut() -> Option<Vec<u8>>> =
1008 Box::new(move || once.take());
1009 let mut chunkname = b"@".to_vec();
1010 chunkname.extend_from_slice(fname);
1011 let status = lua_vm::api::load(state, boxed, Some(&chunkname), mode)?;
1012 Ok(if status == LuaStatus::Ok { 0 } else { status as i32 })
1013}
1014
1015pub fn load_bufferx(
1018 state: &mut LuaState,
1019 buff: &[u8],
1020 name: &[u8],
1021 mode: Option<&[u8]>,
1022) -> Result<i32, LuaError> {
1023 let _reader = make_string_reader(buff.to_vec());
1025 let ok = state.load(buff, name, mode)?;
1026 Ok(if ok { 0 } else { 1 })
1027}
1028
1029pub fn load_buffer(
1032 state: &mut LuaState,
1033 buff: &[u8],
1034 name: &[u8],
1035) -> Result<i32, LuaError> {
1036 load_bufferx(state, buff, name, None)
1037}
1038
1039pub fn load_string(state: &mut LuaState, s: &[u8]) -> Result<i32, LuaError> {
1042 load_buffer(state, s, s)
1043}
1044
1045pub fn get_metafield(
1051 state: &mut LuaState,
1052 obj: i32,
1053 event: &[u8],
1054) -> Result<LuaType, LuaError> {
1055 if !state.get_metatable(obj)? {
1056 return Ok(LuaType::Nil);
1057 }
1058 state.push_bytes(event)?;
1059 let tt = state.raw_get(-2)?;
1060 if tt == LuaType::Nil {
1061 state.pop_n(2);
1062 } else {
1063 state.remove(-2)?;
1064 }
1065 Ok(tt)
1066}
1067
1068pub fn call_meta(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<bool, LuaError> {
1072 let obj = state.abs_index(obj);
1073 if get_metafield(state, obj, event)? == LuaType::Nil {
1074 return Ok(false);
1075 }
1076 state.push_value(obj)?;
1077 state.call(1, 1)?;
1078 Ok(true)
1079}
1080
1081pub fn lua_len(state: &mut LuaState, idx: i32) -> Result<i64, LuaError> {
1085 state.len_op(idx)?;
1086 let l = match state.to_integer_x(-1) {
1087 Some(n) => n,
1088 None => {
1089 return Err(LuaError::runtime(format_args!(
1090 "object length is not an integer"
1091 )));
1092 }
1093 };
1094 state.pop_n(1);
1095 Ok(l)
1096}
1097
1098pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Vec<u8>, LuaError> {
1102 let idx = state.abs_index(idx);
1103 if call_meta(state, idx, b"__tostring")? {
1104 if state.type_at(-1) != LuaType::String {
1105 return Err(LuaError::runtime(format_args!(
1106 "'__tostring' must return a string"
1107 )));
1108 }
1109 } else {
1110 match state.type_at(idx) {
1111 LuaType::Number => {
1112 if state.is_integer(idx) {
1113 let i = state.to_integer_x(idx).unwrap_or(0);
1114 state.push_fstring(format_args!("{}", i))?;
1115 } else {
1116 let f = state.to_number_x(idx).unwrap_or(0.0);
1117 state.push_fstring(format_args!("{:?}", f))?;
1118 }
1119 }
1120 LuaType::String => {
1121 state.push_value(idx)?;
1122 }
1123 LuaType::Boolean => {
1124 let b = state.to_boolean(idx);
1125 state.push_string(if b { b"true" } else { b"false" })?;
1126 }
1127 LuaType::Nil => {
1128 state.push_string(b"nil")?;
1129 }
1130 _ => {
1131 let tt = get_metafield(state, idx, b"__name")?;
1132 let kind: Vec<u8> = if tt == LuaType::String {
1133 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1134 } else {
1135 state.type_name_at(idx).to_vec()
1136 };
1137 state.push_fstring(format_args!("{}: 0x?", BStr(&kind)))?;
1140 if tt != LuaType::Nil {
1141 state.remove(-2)?;
1142 }
1143 }
1144 }
1145 }
1146 Ok(state.peek_bytes(-1).unwrap_or_default())
1147}
1148
1149pub fn set_funcs(
1153 state: &mut LuaState,
1154 l: &[LuaReg],
1155 nup: i32,
1156) -> Result<(), LuaError> {
1157 check_stack(state, nup, Some(b"too many upvalues"))?;
1158 for reg in l {
1159 match reg.func {
1160 None => {
1161 state.push(LuaValue::Bool(false));
1162 }
1163 Some(f) => {
1164 for _ in 0..nup {
1165 state.push_value(-nup)?;
1166 }
1167 state.push_c_closure(f, nup)?;
1168 }
1169 }
1170 state.set_field(-(nup + 2), reg.name)?;
1171 }
1172 state.pop_n(nup as usize);
1173 Ok(())
1174}
1175
1176pub fn get_subtable(
1180 state: &mut LuaState,
1181 idx: i32,
1182 fname: &[u8],
1183) -> Result<bool, LuaError> {
1184 if state.get_field(idx, fname)? == LuaType::Table {
1185 return Ok(true);
1186 }
1187 state.pop_n(1);
1188 let idx = state.abs_index(idx);
1189 let new_tbl = state.new_table();
1190 state.push(LuaValue::Table(new_tbl));
1191 state.push_value(-1)?;
1192 state.set_field(idx, fname)?;
1193 Ok(false)
1194}
1195
1196pub fn requiref(
1201 state: &mut LuaState,
1202 modname: &[u8],
1203 openf: fn(&mut LuaState) -> Result<usize, LuaError>,
1204 glb: bool,
1205) -> Result<(), LuaError> {
1206 get_subtable(state, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
1207 state.get_field(-1, modname)?;
1208 if !state.to_boolean(-1) {
1209 state.pop_n(1);
1210 state.push_c_function(openf)?;
1211 state.push_bytes(modname)?;
1212 state.call(1, 1)?;
1213 state.push_value(-1)?;
1214 state.set_field(-3, modname)?;
1215 }
1216 state.remove(-2)?;
1217 if glb {
1218 state.push_value(-1)?;
1219 state.set_global(modname)?;
1220 }
1221 Ok(())
1222}
1223
1224pub fn get_metatable(state: &mut LuaState, tname: &[u8]) -> Result<LuaType, LuaError> {
1229 state.get_field(LUA_REGISTRYINDEX, tname)
1230}
1231
1232pub fn new_state() -> Result<LuaState, LuaError> {
1238 let _ = default_panic_handler;
1241 let _ = warn_off;
1242 todo!("phase-b: LuaState::new()")
1243}
1244
1245fn default_panic_handler(state: &mut LuaState) -> Result<usize, LuaError> {
1248 let msg = if state.type_at(-1) == LuaType::String {
1249 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1250 } else {
1251 b"error object is not a string".to_vec()
1252 };
1253 eprintln!("PANIC: unprotected error in call to Lua API ({})", BStr(&msg));
1254 Ok(0) }
1256
1257fn warn_off(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1260 check_control(state, message, tocont)?;
1261 Ok(())
1262}
1263
1264fn warn_on(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1267 if check_control(state, message, tocont)? {
1268 return Ok(());
1269 }
1270 eprint!("Lua warning: ");
1271 warn_cont(state, message, tocont)
1272}
1273
1274fn warn_cont(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1277 eprint!("{}", BStr(message));
1278 if tocont {
1280 let _ = (warn_cont as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1281 } else {
1282 eprintln!();
1283 let _ = (warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1284 }
1285 Ok(())
1286}
1287
1288fn check_control(
1292 state: &mut LuaState,
1293 message: &[u8],
1294 tocont: bool,
1295) -> Result<bool, LuaError> {
1296 if tocont || message.first() != Some(&b'@') {
1297 return Ok(false);
1298 }
1299 let cmd = &message[1..];
1300 let _ = state;
1302 if cmd == b"off" {
1303 let _ = warn_off as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1304 } else if cmd == b"on" {
1305 let _ = warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1306 }
1307 Ok(true)
1308}
1309
1310pub fn check_version(state: &mut LuaState, ver: f64, sz: usize) -> Result<(), LuaError> {
1313 const LUAL_NUMSIZES: usize = std::mem::size_of::<i64>() * 16 + std::mem::size_of::<f64>();
1314 if sz != LUAL_NUMSIZES {
1315 return Err(LuaError::runtime(format_args!(
1316 "core and library have incompatible numeric types"
1317 )));
1318 }
1319 let v = state.lua_version();
1320 if (v - ver).abs() > f64::EPSILON {
1321 return Err(LuaError::runtime(format_args!(
1322 "version mismatch: app. needs {}, Lua core provides {}",
1323 ver, v
1324 )));
1325 }
1326 Ok(())
1327}
1328
1329struct BStr<'a>(&'a [u8]);
1337
1338impl<'a> std::fmt::Display for BStr<'a> {
1339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1340 for &b in self.0 {
1341 if b.is_ascii() {
1342 f.write_char(b as char)?;
1343 } else {
1344 write!(f, "\\x{:02x}", b)?;
1345 }
1346 }
1347 Ok(())
1348 }
1349}
1350
1351use std::fmt::Write as _;
1353
1354