1use crate::state_stub::{LuaDebug, LuaState, LuaStateStubExt as _};
22use lua_types::{
23 error::LuaError, gc::GcRef, string::LuaString, userdata::LuaUserData, value::LuaValue,
24 LuaStatus, LuaType,
25};
26
27const LEVELS1: i32 = 10;
31
32const LEVELS2: i32 = 11;
34
35const FREELIST_REF: i64 = 3; pub const LUA_REFNIL: i32 = -1;
41
42pub const LUA_NOREF: i32 = -2;
44
45pub const LUA_ERRFILE: i32 = 6;
47
48pub const LUA_LOADED_TABLE: &[u8] = b"_LOADED";
50
51pub const LUA_PRELOAD_TABLE: &[u8] = b"_PRELOAD";
53
54pub const LUA_GNAME: &[u8] = b"_G";
56
57pub const LUA_FILE_HANDLE: &[u8] = b"FILE*";
59
60const LUA_REGISTRYINDEX: i32 = -1_001_000;
62
63#[expect(
65 dead_code,
66 reason = "ported stdlib helper; not yet wired into the runtime"
67)]
68const LUA_MINSTACK: i32 = 20;
69
70pub struct LuaReg {
78 pub name: &'static [u8],
79 pub func: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
80}
81
82pub struct LuaBuffer {
89 pub data: Vec<u8>,
90}
91
92pub struct LuaStream {
99 pub f: Option<Box<dyn std::io::Read>>,
103 pub closef: Option<fn(&mut LuaState) -> Result<usize, LuaError>>,
105}
106
107fn find_field(state: &mut LuaState, objidx: i32, level: i32) -> Result<bool, LuaError> {
114 if level == 0 || state.type_at(-1) != LuaType::Table {
115 return Ok(false);
116 }
117 state.push(LuaValue::Nil);
118 while state.table_next(-2)? {
119 if state.type_at(-2) == LuaType::String {
120 if state.raw_equal(objidx, -1)? {
121 state.pop_n(1); return Ok(true);
123 } else if find_field(state, objidx, level - 1)? {
124 state.push_string(b".")?; state.replace(-3)?; state.concat(3)?; return Ok(true);
129 }
130 }
131 state.pop_n(1); }
133 Ok(false)
134}
135
136fn push_global_func_name(state: &mut LuaState, ar: &mut LuaDebug) -> Result<bool, LuaError> {
140 let top = state.top_count();
141 state.get_info(b"f", ar)?;
142 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
143 check_stack(state, 6, Some(b"not enough stack"))?;
144 if find_field(state, top + 1, 2)? {
145 if state
146 .peek_bytes(-1)
147 .map_or(false, |n| n.starts_with(b"_G."))
148 {
149 let suffix = state
150 .peek_bytes(-1)
151 .map(|n| n[3..].to_vec())
152 .unwrap_or_default();
153 state.push_bytes(&suffix)?;
154 state.remove(-2)?;
155 }
156 state.copy_value(-1, top + 1)?;
157 lua_vm::api::set_top(state, top + 1)?;
158 Ok(true)
159 } else {
160 lua_vm::api::set_top(state, top)?;
161 Ok(false)
162 }
163}
164
165fn push_global_func_name_from_target(
166 state: &mut LuaState,
167 target: &mut LuaState,
168 ar: &mut LuaDebug,
169) -> Result<bool, LuaError> {
170 let top = state.top_count();
171 target.get_info(b"f", ar)?;
172 let func = target.get_at(target.top_idx() - 1);
173 target.pop_n(1);
174 state.push(func);
175 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
176 check_stack(state, 6, Some(b"not enough stack"))?;
177 if find_field(state, top + 1, 2)? {
178 if state
179 .peek_bytes(-1)
180 .map_or(false, |n| n.starts_with(b"_G."))
181 {
182 let suffix = state
183 .peek_bytes(-1)
184 .map(|n| n[3..].to_vec())
185 .unwrap_or_default();
186 state.push_bytes(&suffix)?;
187 state.remove(-2)?;
188 }
189 state.copy_value(-1, top + 1)?;
190 lua_vm::api::set_top(state, top + 1)?;
191 Ok(true)
192 } else {
193 lua_vm::api::set_top(state, top)?;
194 Ok(false)
195 }
196}
197
198fn push_func_name(
201 state: &mut LuaState,
202 ar: &mut LuaDebug,
203 global_lookup_target: Option<&mut LuaState>,
204) -> Result<(), LuaError> {
205 let namewhat_first = state.global().lua_version == lua_types::LuaVersion::V55;
210 if namewhat_first && !ar.namewhat.is_empty() {
211 let namewhat = ar.namewhat.clone();
212 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
213 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
214 return Ok(());
215 }
216 let found_global = match global_lookup_target {
217 Some(target) => push_global_func_name_from_target(state, target, ar)?,
218 None => push_global_func_name(state, ar)?,
219 };
220 if found_global {
221 let name = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
222 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
223 state.remove(-2)?;
224 } else if !ar.namewhat.is_empty() {
225 let namewhat = ar.namewhat.clone();
226 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
227 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
228 } else if ar.what == b'm' {
229 state.push_string(b"main chunk")?;
230 } else if ar.what != b'C' {
231 let src = ar.short_src.clone();
232 let line = ar.linedefined;
233 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
234 } else {
235 state.push_string(b"?")?;
236 }
237 Ok(())
238}
239
240fn last_level(state: &mut LuaState) -> i32 {
243 let mut ar = LuaDebug::default();
244 let mut li: i32 = 1;
245 let mut le: i32 = 1;
246 while state.get_stack(le, &mut ar) {
247 li = le;
248 le *= 2;
249 }
250 while li < le {
252 let m = (li + le) / 2;
253 if state.get_stack(m, &mut ar) {
254 li = m + 1;
255 } else {
256 le = m;
257 }
258 }
259 le - 1
260}
261
262pub fn traceback(
272 state: &mut LuaState,
273 mut other: Option<&mut LuaState>,
274 msg: Option<&[u8]>,
275 level: i32,
276) -> Result<(), LuaError> {
277 if state.global().lua_version == lua_types::LuaVersion::V51 {
278 return traceback_51(state, other, msg, level);
279 }
280
281 let mut b = LuaBuffer::new();
282 let mut ar = LuaDebug::default();
283 let last = match &mut other {
284 Some(o) => last_level(o),
285 None => last_level(state),
286 };
287 let mut limit2show: i32 = if last - level > LEVELS1 + LEVELS2 {
288 LEVELS1
289 } else {
290 -1
291 };
292 buf_init(state, &mut b);
293 if let Some(m) = msg {
294 add_lstring(&mut b, m);
295 add_char(&mut b, b'\n');
296 }
297 add_lstring(&mut b, b"stack traceback:");
298 let mut level = level;
299 loop {
300 let got = match &mut other {
301 Some(o) => o.get_stack(level, &mut ar),
302 None => state.get_stack(level, &mut ar),
303 };
304 if !got {
305 break;
306 }
307 level += 1;
308 if limit2show == 0 {
309 let n = last - level - LEVELS2 + 1;
310 state.push_fstring(format_args!("\n\t...\t(skipping {} levels)", n))?;
311 add_value(state, &mut b)?;
312 level += n;
313 limit2show = LEVELS2;
314 } else {
315 limit2show -= 1;
316 match &mut other {
317 Some(o) => o.get_info(b"Slnt", &mut ar)?,
318 None => state.get_info(b"Slnt", &mut ar)?,
319 }
320 if ar.currentline <= 0 {
321 let src = ar.short_src.clone();
322 state.push_fstring(format_args!("\n\t{}: in ", BStr(&src)))?;
323 } else {
324 let src = ar.short_src.clone();
325 let line = ar.currentline;
326 state.push_fstring(format_args!("\n\t{}:{}: in ", BStr(&src), line))?;
327 }
328 add_value(state, &mut b)?;
329 match &mut other {
330 Some(o) => push_func_name(state, &mut ar, Some(&mut **o))?,
331 None => push_func_name(state, &mut ar, None)?,
332 }
333 add_value(state, &mut b)?;
334 if ar.istailcall {
335 add_lstring(&mut b, b"\n\t(...tail calls...)");
336 }
337 }
338 }
339 push_result(state, &mut b)?;
340 Ok(())
341}
342
343fn traceback_51(
344 state: &mut LuaState,
345 mut other: Option<&mut LuaState>,
346 msg: Option<&[u8]>,
347 level: i32,
348) -> Result<(), LuaError> {
349 const LEVELS1_51: i32 = 12;
350 const LEVELS2_51: i32 = 10;
351
352 let mut b = LuaBuffer::new();
353 let mut ar = LuaDebug::default();
354 let mut firstpart = true;
355 buf_init(state, &mut b);
356 if let Some(m) = msg {
357 add_lstring(&mut b, m);
358 add_char(&mut b, b'\n');
359 }
360 add_lstring(&mut b, b"stack traceback:");
361
362 let mut level = level;
363 loop {
364 let got = match &mut other {
365 Some(o) => o.get_stack(level, &mut ar),
366 None => state.get_stack(level, &mut ar),
367 };
368 if !got {
369 break;
370 }
371 level += 1;
372 if level > LEVELS1_51 && firstpart {
373 let has_tail = match &mut other {
374 Some(o) => o.get_stack(level + LEVELS2_51, &mut ar),
375 None => state.get_stack(level + LEVELS2_51, &mut ar),
376 };
377 if !has_tail {
378 level -= 1;
379 } else {
380 add_lstring(&mut b, b"\n\t...");
381 while match &mut other {
382 Some(o) => o.get_stack(level + LEVELS2_51, &mut ar),
383 None => state.get_stack(level + LEVELS2_51, &mut ar),
384 } {
385 level += 1;
386 }
387 }
388 firstpart = false;
389 continue;
390 }
391
392 match &mut other {
393 Some(o) => o.get_info(b"Snl", &mut ar)?,
394 None => state.get_info(b"Snl", &mut ar)?,
395 }
396 add_lstring(&mut b, b"\n\t");
397 add_lstring(&mut b, &ar.short_src);
398 add_char(&mut b, b':');
399 if ar.currentline > 0 {
400 state.push_fstring(format_args!("{}:", ar.currentline))?;
401 add_value(state, &mut b)?;
402 }
403 if !ar.namewhat.is_empty() {
404 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
405 state.push_fstring(format_args!(" in function '{}'", BStr(&name)))?;
406 add_value(state, &mut b)?;
407 } else if ar.what == b'm' {
408 add_lstring(&mut b, b" in main chunk");
409 } else if ar.what == b'C' || ar.what == b't' {
410 add_lstring(&mut b, b" ?");
411 } else {
412 let src = ar.short_src.clone();
413 let line = ar.linedefined;
414 state.push_fstring(format_args!(" in function <{}:{}>", BStr(&src), line))?;
415 add_value(state, &mut b)?;
416 }
417 }
418
419 push_result(state, &mut b)?;
420 Ok(())
421}
422
423pub fn arg_error(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> Result<usize, LuaError> {
430 let mut ar = LuaDebug::default();
431 if !state.get_stack(0, &mut ar) {
432 return Err(LuaError::runtime(format_args!(
433 "bad argument #{} ({})",
434 arg,
435 BStr(extramsg)
436 )));
437 }
438 state.get_info(b"n", &mut ar)?;
439 if ar.namewhat == b"method" {
440 arg -= 1; if arg == 0 {
442 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
443 return Err(LuaError::runtime(format_args!(
444 "calling '{}' on bad self ({})",
445 BStr(&name),
446 BStr(extramsg)
447 )));
448 }
449 }
450 let fname = if ar.name.is_none() {
451 if push_global_func_name(state, &mut ar)? {
452 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
453 } else {
454 b"?".to_vec()
455 }
456 } else {
457 ar.name.clone().unwrap_or_else(|| b"?".to_vec())
458 };
459 Err(LuaError::runtime(format_args!(
460 "bad argument #{} to '{}' ({})",
461 arg,
462 BStr(&fname),
463 BStr(extramsg)
464 )))
465}
466
467pub fn type_error_arg(state: &mut LuaState, arg: i32, tname: &[u8]) -> Result<usize, LuaError> {
471 let typearg: Vec<u8> = if get_metafield(state, arg, b"__name")? == LuaType::String {
477 let bytes = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
478 state.pop_n(1);
479 bytes
480 } else if state.type_at(arg) == LuaType::LightUserData {
481 b"light userdata".to_vec()
482 } else if state.type_at(arg) == LuaType::None {
483 b"no value".to_vec()
484 } else {
485 state.type_name_at(arg).to_vec()
486 };
487 let msg_owned = format!("{} expected, got {}", BStr(tname), BStr(&typearg));
488 arg_error(state, arg, msg_owned.as_bytes())
489}
490
491fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
494 let name = state.type_name(tag);
495 type_error_arg(state, arg, name)?;
496 Ok(())
497}
498
499pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
503 let mut ar = LuaDebug::default();
504 if state.get_stack(level, &mut ar) {
505 state.get_info(b"Sl", &mut ar)?;
506 if ar.currentline > 0 {
507 let src = ar.short_src.clone();
508 let line = ar.currentline;
509 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
510 return Ok(());
511 }
512 }
513 state.push_string(b"")?;
514 Ok(())
515}
516
517pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
524 push_where(state, 1)?;
525 let where_str = state.pop_bytes();
526 let full = [where_str.as_slice(), msg].concat();
527 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
528}
529
530pub fn file_result(
535 state: &mut LuaState,
536 stat: bool,
537 fname: Option<&[u8]>,
538) -> Result<usize, LuaError> {
539 if stat {
540 state.push(LuaValue::Bool(true));
541 Ok(1)
542 } else {
543 state.push(LuaValue::Nil);
544 let errmsg = b"(errno unavailable in Rust port)".to_vec();
546 if let Some(name) = fname {
547 let full = [name, b": ".as_slice(), &errmsg].concat();
548 state.push_bytes(&full)?;
549 } else {
550 state.push_bytes(&errmsg)?;
551 }
552 state.push(LuaValue::Int(0));
554 Ok(3)
555 }
556}
557
558pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
563 if stat != 0 {
564 return file_result(state, false, None);
565 }
566 let what = b"exit".as_slice();
567 state.push(LuaValue::Bool(true));
568 state.push_bytes(what)?;
569 state.push(LuaValue::Int(stat as i64));
570 Ok(3)
571}
572
573pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
580 if get_metatable(state, tname)? != LuaType::Nil {
581 return Ok(false); }
583 state.pop_n(1);
584 state.create_table(0, 2)?;
585 state.push_bytes(tname)?;
586 state.set_field(-2, b"__name")?;
587 state.push_value(-1)?;
588 state.set_field(LUA_REGISTRYINDEX, tname)?;
589 Ok(true)
590}
591
592pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
595 get_metatable(state, tname)?;
596 state.set_metatable(-2)?;
597 Ok(())
598}
599
600pub fn test_udata(
604 state: &mut LuaState,
605 ud: i32,
606 tname: &[u8],
607) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
608 let p = state.to_userdata(ud);
609 if let Some(p) = p {
610 if state.get_metatable(ud)? {
611 get_metatable(state, tname)?;
612 let eq = state.raw_equal(-1, -2)?;
613 state.pop_n(2); if eq {
615 return Ok(Some(p));
616 }
617 }
618 }
619 Ok(None)
620}
621
622pub fn check_udata(
625 state: &mut LuaState,
626 ud: i32,
627 tname: &[u8],
628) -> Result<GcRef<LuaUserData>, LuaError> {
629 match test_udata(state, ud, tname)? {
630 Some(p) => Ok(p),
631 None => {
632 type_error_arg(state, ud, tname)?;
633 unreachable!()
634 }
635 }
636}
637
638pub fn check_option(
644 state: &mut LuaState,
645 arg: i32,
646 def: Option<&[u8]>,
647 lst: &[&[u8]],
648) -> Result<usize, LuaError> {
649 let name: Vec<u8> = match def {
650 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
651 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
652 };
653 for (i, entry) in lst.iter().enumerate() {
654 if *entry == name.as_slice() {
655 return Ok(i);
656 }
657 }
658 Err(LuaError::runtime(format_args!(
659 "invalid option '{}'",
660 BStr(&name)
661 )))
662}
663
664pub fn check_stack(state: &mut LuaState, space: i32, msg: Option<&[u8]>) -> Result<(), LuaError> {
667 if !state.check_stack_space(space) {
668 match msg {
669 Some(m) => {
670 return Err(LuaError::runtime(format_args!(
671 "stack overflow ({})",
672 BStr(m)
673 )));
674 }
675 None => {
676 return Err(LuaError::runtime(format_args!("stack overflow")));
677 }
678 }
679 }
680 Ok(())
681}
682
683pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
686 if state.type_at(arg) != t {
687 tag_error(state, arg, t)?;
688 }
689 Ok(())
690}
691
692pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
695 if state.type_at(arg) == LuaType::None {
696 return Err(LuaError::arg_error(arg, "value expected"));
697 }
698 Ok(())
699}
700
701pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
704 match state.to_lua_string(arg) {
705 Some(s) => Ok(s),
706 None => {
707 tag_error(state, arg, LuaType::String)?;
708 unreachable!()
709 }
710 }
711}
712
713pub fn opt_lstring(
716 state: &mut LuaState,
717 arg: i32,
718 def: Option<&[u8]>,
719) -> Result<Option<Vec<u8>>, LuaError> {
720 if state.is_none_or_nil(arg) {
721 return Ok(def.map(|d| d.to_vec()));
722 }
723 let s = check_lstring(state, arg)?;
724 Ok(Some(s.as_bytes().to_vec()))
725}
726
727pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
730 match state.to_number_x(arg) {
731 Some(d) => Ok(d),
732 None => {
733 tag_error(state, arg, LuaType::Number)?;
734 unreachable!()
735 }
736 }
737}
738
739pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
742 if state.is_none_or_nil(arg) {
743 Ok(def)
744 } else {
745 check_number(state, arg)
746 }
747}
748
749fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
755 if state.is_number(arg) {
756 Err(LuaError::arg_error(
757 arg,
758 "number has no integer representation",
759 ))
760 } else {
761 tag_error(state, arg, LuaType::Number)?;
762 unreachable!("tag_error always returns Err")
763 }
764}
765
766pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
769 match state.to_integer_x(arg) {
770 Some(d) => Ok(d),
771 None => {
772 int_error(state, arg)?;
773 unreachable!("int_error always returns Err")
774 }
775 }
776}
777
778pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
781 if state.is_none_or_nil(arg) {
782 Ok(def)
783 } else {
784 check_integer(state, arg)
785 }
786}
787
788impl LuaBuffer {
791 pub fn new() -> Self {
795 LuaBuffer { data: Vec::new() }
796 }
797
798 pub fn len(&self) -> usize {
800 self.data.len()
801 }
802}
803
804impl Default for LuaBuffer {
805 fn default() -> Self {
806 LuaBuffer::new()
807 }
808}
809
810pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
816 *buf = LuaBuffer::new();
820 let _ = state.push(LuaValue::Nil);
822}
823
824pub fn buf_init_size(state: &mut LuaState, buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
827 buf_init(state, buf);
828 buf.data.reserve(sz);
829 Ok(())
830}
831
832fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
836 if usize::MAX - sz < buf.len() {
837 return Err(LuaError::runtime(format_args!("buffer too large")));
838 }
839 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
841 Ok(buf.len() + sz)
842 } else {
843 Ok(newsize)
844 }
845}
846
847pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
850 if buf.data.capacity() - buf.data.len() < sz {
851 let newcap = new_buff_size(buf, sz)?;
852 buf.data.reserve(newcap - buf.data.len());
853 }
854 Ok(())
855}
856
857pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
860 if !s.is_empty() {
861 buf.data.extend_from_slice(s);
862 }
863}
864
865pub fn add_char(buf: &mut LuaBuffer, c: u8) {
868 buf.data.push(c);
869}
870
871pub fn add_size(_buf: &mut LuaBuffer, sz: usize) {
874 let _ = sz;
879}
880
881pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
884 if let Some(bytes) = state.peek_bytes(-1) {
885 let owned = bytes.to_vec();
886 add_lstring(buf, &owned);
887 }
888 state.pop_n(1);
889 Ok(())
890}
891
892pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
895 state.push_bytes(&buf.data)?;
896 state.remove(-2)?;
897 Ok(())
898}
899
900pub fn push_result_size(
903 state: &mut LuaState,
904 buf: &mut LuaBuffer,
905 sz: usize,
906) -> Result<(), LuaError> {
907 add_size(buf, sz);
908 push_result(state, buf)
909}
910
911pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
915 if pat.is_empty() {
916 add_lstring(buf, s);
917 return;
918 }
919 let mut remaining = s;
920 while let Some(pos) = find_bytes(remaining, pat) {
921 add_lstring(buf, &remaining[..pos]);
922 add_lstring(buf, repl);
923 remaining = &remaining[pos + pat.len()..];
924 }
925 add_lstring(buf, remaining);
926}
927
928pub fn gsub<'a>(
932 state: &'a mut LuaState,
933 s: &[u8],
934 pat: &[u8],
935 repl: &[u8],
936) -> Result<Vec<u8>, LuaError> {
937 let mut b = LuaBuffer::new();
938 buf_init(state, &mut b);
939 add_gsub(&mut b, s, pat, repl);
940 push_result(state, &mut b)?;
941 Ok(state.peek_bytes(-1).unwrap_or_default())
942}
943
944fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
948 if needle.is_empty() {
949 return Some(0);
950 }
951 haystack.windows(needle.len()).position(|w| w == needle)
952}
953
954pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
961 if state.type_at(-1) == LuaType::Nil {
962 state.pop_n(1);
963 return Ok(LUA_REFNIL);
964 }
965 let t = state.abs_index(t);
966 let ref_val: i32;
967 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
968 ref_val = 0; state.push(LuaValue::Int(0));
970 state.raw_set_i(t, FREELIST_REF)?;
971 } else {
972 debug_assert!(state.type_at(-1) == LuaType::Number);
973 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
974 }
975 state.pop_n(1); let next_ref: i32;
977 if ref_val != 0 {
978 state.raw_get_i(t, ref_val as i64)?;
979 state.raw_set_i(t, FREELIST_REF)?;
980 next_ref = ref_val;
981 } else {
982 next_ref = (state.raw_len(t) as i32) + 1;
983 }
984 state.raw_set_i(t, next_ref as i64)?;
985 Ok(next_ref)
986}
987
988pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
991 if r >= 0 {
992 let t = state.abs_index(t);
993 state.raw_get_i(t, FREELIST_REF)?;
994 debug_assert!(state.type_at(-1) == LuaType::Number);
995 state.raw_set_i(t, r as i64)?;
996 state.push(LuaValue::Int(r as i64));
997 state.raw_set_i(t, FREELIST_REF)?;
998 }
999 Ok(())
1000}
1001
1002fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
1007 let mut remaining = Some(data);
1008 move || remaining.take()
1009}
1010
1011fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
1020 let s = if buf.starts_with(b"\xEF\xBB\xBF") {
1021 &buf[3..]
1022 } else {
1023 buf
1024 };
1025 if s.first() == Some(&b'#') {
1026 let nl = s
1027 .iter()
1028 .position(|&b| b == b'\n')
1029 .map(|p| p + 1)
1030 .unwrap_or(s.len());
1031 let rest = &s[nl..];
1032 if rest.first() == Some(&0x1B) {
1033 rest.to_vec()
1034 } else {
1035 let mut out = Vec::with_capacity(rest.len() + 1);
1036 out.push(b'\n');
1037 out.extend_from_slice(rest);
1038 out
1039 }
1040 } else {
1041 s.to_vec()
1042 }
1043}
1044
1045pub fn load_filex(
1055 state: &mut LuaState,
1056 filename: Option<&[u8]>,
1057 mode: Option<&[u8]>,
1058) -> Result<i32, LuaError> {
1059 let _ = mode;
1060 let fname = match filename {
1061 Some(f) => f,
1062 None => {
1063 state.push_string(b"cannot read stdin: no filename given")?;
1066 return Ok(LUA_ERRFILE);
1067 }
1068 };
1069 let raw = match state.global().file_loader_hook {
1070 Some(load_fn) => load_fn(fname),
1071 None => Err(LuaError::runtime(format_args!(
1072 "no file_loader_hook registered"
1073 ))),
1074 };
1075 let raw = match raw {
1076 Ok(bytes) => bytes,
1077 Err(e) => {
1078 let detail = match &e {
1079 LuaError::Runtime(LuaValue::Str(s)) => {
1080 String::from_utf8_lossy(s.as_bytes()).into_owned()
1081 }
1082 other => format!("{:?}", other),
1083 };
1084 state.push_fstring(format_args!("cannot open {}: {}", BStr(fname), detail))?;
1085 return Ok(LUA_ERRFILE);
1086 }
1087 };
1088 let payload = skip_bom_and_shebang(&raw);
1089 let mut once = Some(payload);
1090 let boxed: Box<dyn FnMut() -> Option<Vec<u8>>> = Box::new(move || once.take());
1091 let mut chunkname = b"@".to_vec();
1092 chunkname.extend_from_slice(fname);
1093 let status = lua_vm::api::load(state, boxed, Some(&chunkname), mode)?;
1094 Ok(if status == LuaStatus::Ok {
1095 0
1096 } else {
1097 status as i32
1098 })
1099}
1100
1101pub fn load_bufferx(
1104 state: &mut LuaState,
1105 buff: &[u8],
1106 name: &[u8],
1107 mode: Option<&[u8]>,
1108) -> Result<i32, LuaError> {
1109 let _reader = make_string_reader(buff.to_vec());
1111 let ok = state.load(buff, name, mode)?;
1112 Ok(if ok { 0 } else { 1 })
1113}
1114
1115pub fn load_buffer(state: &mut LuaState, buff: &[u8], name: &[u8]) -> Result<i32, LuaError> {
1118 load_bufferx(state, buff, name, None)
1119}
1120
1121pub fn load_string(state: &mut LuaState, s: &[u8]) -> Result<i32, LuaError> {
1124 load_buffer(state, s, s)
1125}
1126
1127pub fn get_metafield(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<LuaType, LuaError> {
1133 if !state.get_metatable(obj)? {
1134 return Ok(LuaType::Nil);
1135 }
1136 state.push_bytes(event)?;
1137 let tt = state.raw_get(-2)?;
1138 if tt == LuaType::Nil {
1139 state.pop_n(2);
1140 } else {
1141 state.remove(-2)?;
1142 }
1143 Ok(tt)
1144}
1145
1146pub fn call_meta(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<bool, LuaError> {
1150 let obj = state.abs_index(obj);
1151 if get_metafield(state, obj, event)? == LuaType::Nil {
1152 return Ok(false);
1153 }
1154 state.push_value(obj)?;
1155 state.call(1, 1)?;
1156 Ok(true)
1157}
1158
1159pub fn lua_len(state: &mut LuaState, idx: i32) -> Result<i64, LuaError> {
1163 state.len_op(idx)?;
1164 let l = match state.to_integer_x(-1) {
1165 Some(n) => n,
1166 None => {
1167 return Err(LuaError::runtime(format_args!(
1168 "object length is not an integer"
1169 )));
1170 }
1171 };
1172 state.pop_n(1);
1173 Ok(l)
1174}
1175
1176pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Vec<u8>, LuaError> {
1180 let idx = state.abs_index(idx);
1181 if call_meta(state, idx, b"__tostring")? {
1182 if state.type_at(-1) != LuaType::String {
1183 return Err(LuaError::runtime(format_args!(
1184 "'__tostring' must return a string"
1185 )));
1186 }
1187 } else {
1188 match state.type_at(idx) {
1189 LuaType::Number => {
1190 if state.is_integer(idx) {
1191 let i = state.to_integer_x(idx).unwrap_or(0);
1192 state.push_fstring(format_args!("{}", i))?;
1193 } else {
1194 let f = state.to_number_x(idx).unwrap_or(0.0);
1195 state.push_fstring(format_args!("{:?}", f))?;
1196 }
1197 }
1198 LuaType::String => {
1199 state.push_value(idx)?;
1200 }
1201 LuaType::Boolean => {
1202 let b = state.to_boolean(idx);
1203 state.push_string(if b { b"true" } else { b"false" })?;
1204 }
1205 LuaType::Nil => {
1206 state.push_string(b"nil")?;
1207 }
1208 _ => {
1209 let tt = get_metafield(state, idx, b"__name")?;
1210 let kind: Vec<u8> = if tt == LuaType::String {
1211 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1212 } else {
1213 state.type_name_at(idx).to_vec()
1214 };
1215 state.push_fstring(format_args!("{}: 0x?", BStr(&kind)))?;
1218 if tt != LuaType::Nil {
1219 state.remove(-2)?;
1220 }
1221 }
1222 }
1223 }
1224 Ok(state.peek_bytes(-1).unwrap_or_default())
1225}
1226
1227pub fn set_funcs(state: &mut LuaState, l: &[LuaReg], nup: i32) -> Result<(), LuaError> {
1231 check_stack(state, nup, Some(b"too many upvalues"))?;
1232 for reg in l {
1233 match reg.func {
1234 None => {
1235 state.push(LuaValue::Bool(false));
1236 }
1237 Some(f) => {
1238 for _ in 0..nup {
1239 state.push_value(-nup)?;
1240 }
1241 state.push_c_closure(f, nup)?;
1242 }
1243 }
1244 state.set_field(-(nup + 2), reg.name)?;
1245 }
1246 state.pop_n(nup as usize);
1247 Ok(())
1248}
1249
1250pub fn get_subtable(state: &mut LuaState, idx: i32, fname: &[u8]) -> Result<bool, LuaError> {
1254 if state.get_field(idx, fname)? == LuaType::Table {
1255 return Ok(true);
1256 }
1257 state.pop_n(1);
1258 let idx = state.abs_index(idx);
1259 let new_tbl = state.new_table();
1260 state.push(LuaValue::Table(new_tbl));
1261 state.push_value(-1)?;
1262 state.set_field(idx, fname)?;
1263 Ok(false)
1264}
1265
1266pub fn requiref(
1271 state: &mut LuaState,
1272 modname: &[u8],
1273 openf: fn(&mut LuaState) -> Result<usize, LuaError>,
1274 glb: bool,
1275) -> Result<(), LuaError> {
1276 get_subtable(state, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
1277 state.get_field(-1, modname)?;
1278 if !state.to_boolean(-1) {
1279 state.pop_n(1);
1280 state.push_c_function(openf)?;
1281 state.push_bytes(modname)?;
1282 state.call(1, 1)?;
1283 state.push_value(-1)?;
1284 state.set_field(-3, modname)?;
1285 }
1286 state.remove(-2)?;
1287 if glb {
1288 state.push_value(-1)?;
1289 state.set_global(modname)?;
1290 }
1291 Ok(())
1292}
1293
1294pub fn get_metatable(state: &mut LuaState, tname: &[u8]) -> Result<LuaType, LuaError> {
1299 state.get_field(LUA_REGISTRYINDEX, tname)
1300}
1301
1302pub fn new_state() -> Result<LuaState, LuaError> {
1308 let _ = default_panic_handler;
1311 let _ = warn_off;
1312 todo!("phase-b: LuaState::new()")
1313}
1314
1315fn default_panic_handler(state: &mut LuaState) -> Result<usize, LuaError> {
1318 let msg = if state.type_at(-1) == LuaType::String {
1319 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1320 } else {
1321 b"error object is not a string".to_vec()
1322 };
1323 eprintln!(
1324 "PANIC: unprotected error in call to Lua API ({})",
1325 BStr(&msg)
1326 );
1327 Ok(0) }
1329
1330fn warn_off(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1333 check_control(state, message, tocont)?;
1334 Ok(())
1335}
1336
1337fn warn_on(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1340 if check_control(state, message, tocont)? {
1341 return Ok(());
1342 }
1343 eprint!("Lua warning: ");
1344 warn_cont(state, message, tocont)
1345}
1346
1347fn warn_cont(_state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1350 eprint!("{}", BStr(message));
1351 if tocont {
1353 let _ = (warn_cont as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1354 } else {
1355 eprintln!();
1356 let _ = (warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1357 }
1358 Ok(())
1359}
1360
1361fn check_control(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<bool, LuaError> {
1365 if tocont || message.first() != Some(&b'@') {
1366 return Ok(false);
1367 }
1368 let cmd = &message[1..];
1369 let _ = state;
1371 if cmd == b"off" {
1372 let _ = warn_off as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1373 } else if cmd == b"on" {
1374 let _ = warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1375 }
1376 Ok(true)
1377}
1378
1379pub fn check_version(state: &mut LuaState, ver: f64, sz: usize) -> Result<(), LuaError> {
1382 const LUAL_NUMSIZES: usize = std::mem::size_of::<i64>() * 16 + std::mem::size_of::<f64>();
1383 if sz != LUAL_NUMSIZES {
1384 return Err(LuaError::runtime(format_args!(
1385 "core and library have incompatible numeric types"
1386 )));
1387 }
1388 let v = state.lua_version();
1389 if (v - ver).abs() > f64::EPSILON {
1390 return Err(LuaError::runtime(format_args!(
1391 "version mismatch: app. needs {}, Lua core provides {}",
1392 ver, v
1393 )));
1394 }
1395 Ok(())
1396}
1397
1398struct BStr<'a>(&'a [u8]);
1406
1407impl<'a> std::fmt::Display for BStr<'a> {
1408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1409 for &b in self.0 {
1410 if b.is_ascii() {
1411 f.write_char(b as char)?;
1412 } else {
1413 write!(f, "\\x{:02x}", b)?;
1414 }
1415 }
1416 Ok(())
1417 }
1418}
1419
1420use std::fmt::Write as _;
1422
1423