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 if state.global().lua_version == lua_types::LuaVersion::V51 {
141 return Ok(false);
142 }
143 let top = state.top_count();
144 state.get_info(b"f", ar)?;
145 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
146 check_stack(state, 6, Some(b"not enough stack"))?;
147 if find_field(state, top + 1, 2)? {
148 if state
149 .peek_bytes(-1)
150 .map_or(false, |n| n.starts_with(b"_G."))
151 {
152 let suffix = state
153 .peek_bytes(-1)
154 .map(|n| n[3..].to_vec())
155 .unwrap_or_default();
156 state.push_bytes(&suffix)?;
157 state.remove(-2)?;
158 }
159 state.copy_value(-1, top + 1)?;
160 lua_vm::api::set_top(state, top + 1)?;
161 Ok(true)
162 } else {
163 lua_vm::api::set_top(state, top)?;
164 Ok(false)
165 }
166}
167
168fn push_global_func_name_from_target(
169 state: &mut LuaState,
170 target: &mut LuaState,
171 ar: &mut LuaDebug,
172) -> Result<bool, LuaError> {
173 if state.global().lua_version == lua_types::LuaVersion::V51 {
174 return Ok(false);
175 }
176 let top = state.top_count();
177 target.get_info(b"f", ar)?;
178 let func = target.get_at(target.top_idx() - 1);
179 target.pop_n(1);
180 state.push(func);
181 state.get_field(LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
182 check_stack(state, 6, Some(b"not enough stack"))?;
183 if find_field(state, top + 1, 2)? {
184 if state
185 .peek_bytes(-1)
186 .map_or(false, |n| n.starts_with(b"_G."))
187 {
188 let suffix = state
189 .peek_bytes(-1)
190 .map(|n| n[3..].to_vec())
191 .unwrap_or_default();
192 state.push_bytes(&suffix)?;
193 state.remove(-2)?;
194 }
195 state.copy_value(-1, top + 1)?;
196 lua_vm::api::set_top(state, top + 1)?;
197 Ok(true)
198 } else {
199 lua_vm::api::set_top(state, top)?;
200 Ok(false)
201 }
202}
203
204fn push_func_name(
213 state: &mut LuaState,
214 ar: &mut LuaDebug,
215 global_lookup_target: Option<&mut LuaState>,
216) -> Result<(), LuaError> {
217 if state.global().lua_version == lua_types::LuaVersion::V52 {
218 if !ar.namewhat.is_empty() {
219 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
220 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
221 } else if ar.what == b'm' {
222 state.push_string(b"main chunk")?;
223 } else if ar.what == b'C' {
224 state.push_string(b"?")?;
225 } else {
226 let src = ar.short_src.clone();
227 let line = ar.linedefined;
228 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
229 }
230 return Ok(());
231 }
232 let namewhat_first = state.global().lua_version == lua_types::LuaVersion::V55;
237 if namewhat_first && !ar.namewhat.is_empty() {
238 let namewhat = ar.namewhat.clone();
239 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
240 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
241 return Ok(());
242 }
243 let found_global = match global_lookup_target {
244 Some(target) => push_global_func_name_from_target(state, target, ar)?,
245 None => push_global_func_name(state, ar)?,
246 };
247 if found_global {
248 let name = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
249 state.push_fstring(format_args!("function '{}'", BStr(&name)))?;
250 state.remove(-2)?;
251 } else if !ar.namewhat.is_empty() {
252 let namewhat = ar.namewhat.clone();
253 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
254 state.push_fstring(format_args!("{} '{}'", BStr(&namewhat), BStr(&name)))?;
255 } else if ar.what == b'm' {
256 state.push_string(b"main chunk")?;
257 } else if ar.what != b'C' {
258 let src = ar.short_src.clone();
259 let line = ar.linedefined;
260 state.push_fstring(format_args!("function <{}:{}>", BStr(&src), line))?;
261 } else {
262 state.push_string(b"?")?;
263 }
264 Ok(())
265}
266
267fn last_level(state: &mut LuaState) -> i32 {
270 let mut ar = LuaDebug::default();
271 let mut li: i32 = 1;
272 let mut le: i32 = 1;
273 while state.get_stack(le, &mut ar) {
274 li = le;
275 le *= 2;
276 }
277 while li < le {
279 let m = (li + le) / 2;
280 if state.get_stack(m, &mut ar) {
281 li = m + 1;
282 } else {
283 le = m;
284 }
285 }
286 le - 1
287}
288
289pub fn traceback(
299 state: &mut LuaState,
300 mut other: Option<&mut LuaState>,
301 msg: Option<&[u8]>,
302 level: i32,
303) -> Result<(), LuaError> {
304 if state.global().lua_version == lua_types::LuaVersion::V51 {
305 return traceback_51(state, other, msg, level);
306 }
307
308 let mut b = LuaBuffer::new();
309 let mut ar = LuaDebug::default();
310 let last = match &mut other {
311 Some(o) => last_level(o),
312 None => last_level(state),
313 };
314 let mut limit2show: i32 = if last - level > LEVELS1 + LEVELS2 {
315 LEVELS1
316 } else {
317 -1
318 };
319 buf_init(state, &mut b);
320 if let Some(m) = msg {
321 add_lstring(&mut b, m);
322 add_char(&mut b, b'\n');
323 }
324 add_lstring(&mut b, b"stack traceback:");
325 let mut level = level;
326 loop {
327 let got = match &mut other {
328 Some(o) => o.get_stack(level, &mut ar),
329 None => state.get_stack(level, &mut ar),
330 };
331 if !got {
332 break;
333 }
334 level += 1;
335 if limit2show == 0 {
336 let n = last - level - LEVELS2 + 1;
337 state.push_fstring(format_args!("\n\t...\t(skipping {} levels)", n))?;
338 add_value(state, &mut b)?;
339 level += n;
340 limit2show = LEVELS2;
341 } else {
342 limit2show -= 1;
343 match &mut other {
344 Some(o) => o.get_info(b"Slnt", &mut ar)?,
345 None => state.get_info(b"Slnt", &mut ar)?,
346 }
347 if ar.currentline <= 0 {
348 let src = ar.short_src.clone();
349 state.push_fstring(format_args!("\n\t{}: in ", BStr(&src)))?;
350 } else {
351 let src = ar.short_src.clone();
352 let line = ar.currentline;
353 state.push_fstring(format_args!("\n\t{}:{}: in ", BStr(&src), line))?;
354 }
355 add_value(state, &mut b)?;
356 match &mut other {
357 Some(o) => push_func_name(state, &mut ar, Some(&mut **o))?,
358 None => push_func_name(state, &mut ar, None)?,
359 }
360 add_value(state, &mut b)?;
361 if ar.istailcall {
362 add_lstring(&mut b, b"\n\t(...tail calls...)");
363 }
364 }
365 }
366 push_result(state, &mut b)?;
367 Ok(())
368}
369
370fn traceback_51(
371 state: &mut LuaState,
372 mut other: Option<&mut LuaState>,
373 msg: Option<&[u8]>,
374 level: i32,
375) -> Result<(), LuaError> {
376 const LEVELS1_51: i32 = 12;
377 const LEVELS2_51: i32 = 10;
378
379 let mut b = LuaBuffer::new();
380 let mut ar = LuaDebug::default();
381 let mut firstpart = true;
382 buf_init(state, &mut b);
383 if let Some(m) = msg {
384 add_lstring(&mut b, m);
385 add_char(&mut b, b'\n');
386 }
387 add_lstring(&mut b, b"stack traceback:");
388
389 let mut level = level;
390 loop {
391 let got = match &mut other {
392 Some(o) => o.get_stack(level, &mut ar),
393 None => state.get_stack(level, &mut ar),
394 };
395 if !got {
396 break;
397 }
398 level += 1;
399 if level > LEVELS1_51 && firstpart {
400 let has_tail = match &mut other {
401 Some(o) => o.get_stack(level + LEVELS2_51, &mut ar),
402 None => state.get_stack(level + LEVELS2_51, &mut ar),
403 };
404 if !has_tail {
405 level -= 1;
406 } else {
407 add_lstring(&mut b, b"\n\t...");
408 while match &mut other {
409 Some(o) => o.get_stack(level + LEVELS2_51, &mut ar),
410 None => state.get_stack(level + LEVELS2_51, &mut ar),
411 } {
412 level += 1;
413 }
414 }
415 firstpart = false;
416 continue;
417 }
418
419 match &mut other {
420 Some(o) => o.get_info(b"Snl", &mut ar)?,
421 None => state.get_info(b"Snl", &mut ar)?,
422 }
423 add_lstring(&mut b, b"\n\t");
424 add_lstring(&mut b, &ar.short_src);
425 add_char(&mut b, b':');
426 if ar.currentline > 0 {
427 state.push_fstring(format_args!("{}:", ar.currentline))?;
428 add_value(state, &mut b)?;
429 }
430 if !ar.namewhat.is_empty() {
431 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
432 state.push_fstring(format_args!(" in function '{}'", BStr(&name)))?;
433 add_value(state, &mut b)?;
434 } else if ar.what == b'm' {
435 add_lstring(&mut b, b" in main chunk");
436 } else if ar.what == b'C' || ar.what == b't' {
437 add_lstring(&mut b, b" ?");
438 } else {
439 let src = ar.short_src.clone();
440 let line = ar.linedefined;
441 state.push_fstring(format_args!(" in function <{}:{}>", BStr(&src), line))?;
442 add_value(state, &mut b)?;
443 }
444 }
445
446 push_result(state, &mut b)?;
447 Ok(())
448}
449
450pub fn arg_error(state: &mut LuaState, mut arg: i32, extramsg: &[u8]) -> Result<usize, LuaError> {
457 let mut ar = LuaDebug::default();
458 if !state.get_stack(0, &mut ar) {
459 return Err(LuaError::runtime(format_args!(
460 "bad argument #{} ({})",
461 arg,
462 BStr(extramsg)
463 )));
464 }
465 state.get_info(b"n", &mut ar)?;
466 if ar.namewhat == b"method" {
467 arg -= 1; if arg == 0 {
469 let name = ar.name.clone().unwrap_or_else(|| b"?".to_vec());
470 return Err(LuaError::runtime(format_args!(
471 "calling '{}' on bad self ({})",
472 BStr(&name),
473 BStr(extramsg)
474 )));
475 }
476 }
477 let fname = if ar.name.is_none() {
478 if push_global_func_name(state, &mut ar)? {
479 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
480 } else {
481 b"?".to_vec()
482 }
483 } else {
484 ar.name.clone().unwrap_or_else(|| b"?".to_vec())
485 };
486 Err(LuaError::runtime(format_args!(
487 "bad argument #{} to '{}' ({})",
488 arg,
489 BStr(&fname),
490 BStr(extramsg)
491 )))
492}
493
494pub fn type_error_arg(state: &mut LuaState, arg: i32, tname: &[u8]) -> Result<usize, LuaError> {
498 let honors_name = state.global().lua_version.honors_name_metafield();
504 let typearg: Vec<u8> = if honors_name && get_metafield(state, arg, b"__name")? == LuaType::String {
505 let bytes = state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec());
506 state.pop_n(1);
507 bytes
508 } else if state.type_at(arg) == LuaType::LightUserData {
509 b"light userdata".to_vec()
510 } else if state.type_at(arg) == LuaType::None {
511 b"no value".to_vec()
512 } else {
513 state.type_name_at(arg).to_vec()
514 };
515 let msg_owned = format!("{} expected, got {}", BStr(tname), BStr(&typearg));
516 arg_error(state, arg, msg_owned.as_bytes())
517}
518
519fn tag_error(state: &mut LuaState, arg: i32, tag: LuaType) -> Result<(), LuaError> {
522 let name = state.type_name(tag);
523 type_error_arg(state, arg, name)?;
524 Ok(())
525}
526
527pub fn push_where(state: &mut LuaState, level: i32) -> Result<(), LuaError> {
531 let mut ar = LuaDebug::default();
532 if state.get_stack(level, &mut ar) {
533 state.get_info(b"Sl", &mut ar)?;
534 if ar.currentline > 0 {
535 let src = ar.short_src.clone();
536 let line = ar.currentline;
537 state.push_fstring(format_args!("{}:{}: ", BStr(&src), line))?;
538 return Ok(());
539 }
540 }
541 state.push_string(b"")?;
542 Ok(())
543}
544
545pub fn lua_error(state: &mut LuaState, msg: &[u8]) -> Result<usize, LuaError> {
552 push_where(state, 1)?;
553 let where_str = state.pop_bytes();
554 let full = [where_str.as_slice(), msg].concat();
555 Err(LuaError::runtime(format_args!("{}", BStr(&full))))
556}
557
558pub fn file_result(
563 state: &mut LuaState,
564 stat: bool,
565 fname: Option<&[u8]>,
566) -> Result<usize, LuaError> {
567 if stat {
568 state.push(LuaValue::Bool(true));
569 Ok(1)
570 } else {
571 state.push(LuaValue::Nil);
572 let errmsg = b"(errno unavailable in Rust port)".to_vec();
574 if let Some(name) = fname {
575 let full = [name, b": ".as_slice(), &errmsg].concat();
576 state.push_bytes(&full)?;
577 } else {
578 state.push_bytes(&errmsg)?;
579 }
580 state.push(LuaValue::Int(0));
582 Ok(3)
583 }
584}
585
586pub fn exec_result(state: &mut LuaState, stat: i32) -> Result<usize, LuaError> {
591 if stat != 0 {
592 return file_result(state, false, None);
593 }
594 let what = b"exit".as_slice();
595 state.push(LuaValue::Bool(true));
596 state.push_bytes(what)?;
597 state.push(LuaValue::Int(stat as i64));
598 Ok(3)
599}
600
601pub fn new_metatable(state: &mut LuaState, tname: &[u8]) -> Result<bool, LuaError> {
608 if get_metatable(state, tname)? != LuaType::Nil {
609 return Ok(false); }
611 state.pop_n(1);
612 state.create_table(0, 2)?;
613 state.push_bytes(tname)?;
614 state.set_field(-2, b"__name")?;
615 state.push_value(-1)?;
616 state.set_field(LUA_REGISTRYINDEX, tname)?;
617 Ok(true)
618}
619
620pub fn set_metatable(state: &mut LuaState, tname: &[u8]) -> Result<(), LuaError> {
623 get_metatable(state, tname)?;
624 state.set_metatable(-2)?;
625 Ok(())
626}
627
628pub fn test_udata(
632 state: &mut LuaState,
633 ud: i32,
634 tname: &[u8],
635) -> Result<Option<GcRef<LuaUserData>>, LuaError> {
636 let p = state.to_userdata(ud);
637 if let Some(p) = p {
638 if state.get_metatable(ud)? {
639 get_metatable(state, tname)?;
640 let eq = state.raw_equal(-1, -2)?;
641 state.pop_n(2); if eq {
643 return Ok(Some(p));
644 }
645 }
646 }
647 Ok(None)
648}
649
650pub fn check_udata(
653 state: &mut LuaState,
654 ud: i32,
655 tname: &[u8],
656) -> Result<GcRef<LuaUserData>, LuaError> {
657 match test_udata(state, ud, tname)? {
658 Some(p) => Ok(p),
659 None => {
660 type_error_arg(state, ud, tname)?;
661 unreachable!()
662 }
663 }
664}
665
666pub fn check_option(
672 state: &mut LuaState,
673 arg: i32,
674 def: Option<&[u8]>,
675 lst: &[&[u8]],
676) -> Result<usize, LuaError> {
677 let name: Vec<u8> = match def {
678 Some(d) if state.is_none_or_nil(arg) => d.to_vec(),
679 _ => check_lstring(state, arg)?.as_bytes().to_vec(),
680 };
681 for (i, entry) in lst.iter().enumerate() {
682 if *entry == name.as_slice() {
683 return Ok(i);
684 }
685 }
686 Err(LuaError::runtime(format_args!(
687 "invalid option '{}'",
688 BStr(&name)
689 )))
690}
691
692pub fn check_stack(state: &mut LuaState, space: i32, msg: Option<&[u8]>) -> Result<(), LuaError> {
695 if !state.check_stack_space(space) {
696 match msg {
697 Some(m) => {
698 return Err(LuaError::runtime(format_args!(
699 "stack overflow ({})",
700 BStr(m)
701 )));
702 }
703 None => {
704 return Err(LuaError::runtime(format_args!("stack overflow")));
705 }
706 }
707 }
708 Ok(())
709}
710
711pub fn check_type(state: &mut LuaState, arg: i32, t: LuaType) -> Result<(), LuaError> {
714 if state.type_at(arg) != t {
715 tag_error(state, arg, t)?;
716 }
717 Ok(())
718}
719
720pub fn check_any(state: &mut LuaState, arg: i32) -> Result<(), LuaError> {
723 if state.type_at(arg) == LuaType::None {
724 arg_error(state, arg, b"value expected")?;
725 }
726 Ok(())
727}
728
729pub fn check_lstring(state: &mut LuaState, arg: i32) -> Result<GcRef<LuaString>, LuaError> {
732 match state.to_lua_string(arg) {
733 Some(s) => Ok(s),
734 None => {
735 tag_error(state, arg, LuaType::String)?;
736 unreachable!()
737 }
738 }
739}
740
741pub fn opt_lstring(
744 state: &mut LuaState,
745 arg: i32,
746 def: Option<&[u8]>,
747) -> Result<Option<Vec<u8>>, LuaError> {
748 if state.is_none_or_nil(arg) {
749 return Ok(def.map(|d| d.to_vec()));
750 }
751 let s = check_lstring(state, arg)?;
752 Ok(Some(s.as_bytes().to_vec()))
753}
754
755pub fn check_number(state: &mut LuaState, arg: i32) -> Result<f64, LuaError> {
758 match state.to_number_x(arg) {
759 Some(d) => Ok(d),
760 None => {
761 tag_error(state, arg, LuaType::Number)?;
762 unreachable!()
763 }
764 }
765}
766
767pub fn opt_number(state: &mut LuaState, arg: i32, def: f64) -> Result<f64, LuaError> {
770 if state.is_none_or_nil(arg) {
771 Ok(def)
772 } else {
773 check_number(state, arg)
774 }
775}
776
777fn int_error(state: &mut LuaState, arg: i32) -> Result<usize, LuaError> {
783 if state.is_number(arg) {
784 arg_error(state, arg, b"number has no integer representation")
785 } else {
786 tag_error(state, arg, LuaType::Number)?;
787 unreachable!("tag_error always returns Err")
788 }
789}
790
791pub fn check_integer(state: &mut LuaState, arg: i32) -> Result<i64, LuaError> {
794 match state.to_integer_x(arg) {
795 Some(d) => Ok(d),
796 None => {
797 int_error(state, arg)?;
798 unreachable!("int_error always returns Err")
799 }
800 }
801}
802
803pub fn opt_integer(state: &mut LuaState, arg: i32, def: i64) -> Result<i64, LuaError> {
806 if state.is_none_or_nil(arg) {
807 Ok(def)
808 } else {
809 check_integer(state, arg)
810 }
811}
812
813impl LuaBuffer {
816 pub fn new() -> Self {
820 LuaBuffer { data: Vec::new() }
821 }
822
823 pub fn len(&self) -> usize {
825 self.data.len()
826 }
827}
828
829impl Default for LuaBuffer {
830 fn default() -> Self {
831 LuaBuffer::new()
832 }
833}
834
835pub fn buf_init(state: &mut LuaState, buf: &mut LuaBuffer) {
841 *buf = LuaBuffer::new();
845 let _ = state.push(LuaValue::Nil);
847}
848
849pub fn buf_init_size(state: &mut LuaState, buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
852 buf_init(state, buf);
853 buf.data.reserve(sz);
854 Ok(())
855}
856
857fn new_buff_size(buf: &LuaBuffer, sz: usize) -> Result<usize, LuaError> {
861 if usize::MAX - sz < buf.len() {
862 return Err(LuaError::runtime(format_args!("buffer too large")));
863 }
864 let newsize = (buf.data.capacity() / 2) * 3; if newsize < buf.len() + sz {
866 Ok(buf.len() + sz)
867 } else {
868 Ok(newsize)
869 }
870}
871
872pub fn prep_buff_size(buf: &mut LuaBuffer, sz: usize) -> Result<(), LuaError> {
875 if buf.data.capacity() - buf.data.len() < sz {
876 let newcap = new_buff_size(buf, sz)?;
877 buf.data.reserve(newcap - buf.data.len());
878 }
879 Ok(())
880}
881
882pub fn add_lstring(buf: &mut LuaBuffer, s: &[u8]) {
885 if !s.is_empty() {
886 buf.data.extend_from_slice(s);
887 }
888}
889
890pub fn add_char(buf: &mut LuaBuffer, c: u8) {
893 buf.data.push(c);
894}
895
896pub fn add_size(_buf: &mut LuaBuffer, sz: usize) {
899 let _ = sz;
904}
905
906pub fn add_value(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
909 if let Some(bytes) = state.peek_bytes(-1) {
910 let owned = bytes.to_vec();
911 add_lstring(buf, &owned);
912 }
913 state.pop_n(1);
914 Ok(())
915}
916
917pub fn push_result(state: &mut LuaState, buf: &mut LuaBuffer) -> Result<(), LuaError> {
920 state.push_bytes(&buf.data)?;
921 state.remove(-2)?;
922 Ok(())
923}
924
925pub fn push_result_size(
928 state: &mut LuaState,
929 buf: &mut LuaBuffer,
930 sz: usize,
931) -> Result<(), LuaError> {
932 add_size(buf, sz);
933 push_result(state, buf)
934}
935
936pub fn add_gsub(buf: &mut LuaBuffer, s: &[u8], pat: &[u8], repl: &[u8]) {
940 if pat.is_empty() {
941 add_lstring(buf, s);
942 return;
943 }
944 let mut remaining = s;
945 while let Some(pos) = find_bytes(remaining, pat) {
946 add_lstring(buf, &remaining[..pos]);
947 add_lstring(buf, repl);
948 remaining = &remaining[pos + pat.len()..];
949 }
950 add_lstring(buf, remaining);
951}
952
953pub fn gsub<'a>(
957 state: &'a mut LuaState,
958 s: &[u8],
959 pat: &[u8],
960 repl: &[u8],
961) -> Result<Vec<u8>, LuaError> {
962 let mut b = LuaBuffer::new();
963 buf_init(state, &mut b);
964 add_gsub(&mut b, s, pat, repl);
965 push_result(state, &mut b)?;
966 Ok(state.peek_bytes(-1).unwrap_or_default())
967}
968
969fn find_bytes(haystack: &[u8], needle: &[u8]) -> Option<usize> {
973 if needle.is_empty() {
974 return Some(0);
975 }
976 haystack.windows(needle.len()).position(|w| w == needle)
977}
978
979pub fn lua_ref(state: &mut LuaState, t: i32) -> Result<i32, LuaError> {
986 if state.type_at(-1) == LuaType::Nil {
987 state.pop_n(1);
988 return Ok(LUA_REFNIL);
989 }
990 let t = state.abs_index(t);
991 let ref_val: i32;
992 if state.raw_get_i(t, FREELIST_REF)? == LuaType::Nil {
993 ref_val = 0; state.push(LuaValue::Int(0));
995 state.raw_set_i(t, FREELIST_REF)?;
996 } else {
997 debug_assert!(state.type_at(-1) == LuaType::Number);
998 ref_val = state.to_integer_x(-1).unwrap_or(0) as i32;
999 }
1000 state.pop_n(1); let next_ref: i32;
1002 if ref_val != 0 {
1003 state.raw_get_i(t, ref_val as i64)?;
1004 state.raw_set_i(t, FREELIST_REF)?;
1005 next_ref = ref_val;
1006 } else {
1007 next_ref = (state.raw_len(t) as i32) + 1;
1008 }
1009 state.raw_set_i(t, next_ref as i64)?;
1010 Ok(next_ref)
1011}
1012
1013pub fn lua_unref(state: &mut LuaState, t: i32, r: i32) -> Result<(), LuaError> {
1016 if r >= 0 {
1017 let t = state.abs_index(t);
1018 state.raw_get_i(t, FREELIST_REF)?;
1019 debug_assert!(state.type_at(-1) == LuaType::Number);
1020 state.raw_set_i(t, r as i64)?;
1021 state.push(LuaValue::Int(r as i64));
1022 state.raw_set_i(t, FREELIST_REF)?;
1023 }
1024 Ok(())
1025}
1026
1027fn make_string_reader(data: Vec<u8>) -> impl FnMut() -> Option<Vec<u8>> {
1032 let mut remaining = Some(data);
1033 move || remaining.take()
1034}
1035
1036fn skip_bom_and_shebang(buf: &[u8]) -> Vec<u8> {
1045 let s = if buf.starts_with(b"\xEF\xBB\xBF") {
1046 &buf[3..]
1047 } else {
1048 buf
1049 };
1050 if s.first() == Some(&b'#') {
1051 let nl = s
1052 .iter()
1053 .position(|&b| b == b'\n')
1054 .map(|p| p + 1)
1055 .unwrap_or(s.len());
1056 let rest = &s[nl..];
1057 if rest.first() == Some(&0x1B) {
1058 rest.to_vec()
1059 } else {
1060 let mut out = Vec::with_capacity(rest.len() + 1);
1061 out.push(b'\n');
1062 out.extend_from_slice(rest);
1063 out
1064 }
1065 } else {
1066 s.to_vec()
1067 }
1068}
1069
1070pub fn load_filex(
1080 state: &mut LuaState,
1081 filename: Option<&[u8]>,
1082 mode: Option<&[u8]>,
1083) -> Result<i32, LuaError> {
1084 let _ = mode;
1085 let fname = match filename {
1086 Some(f) => f,
1087 None => {
1088 state.push_string(b"cannot read stdin: no filename given")?;
1091 return Ok(LUA_ERRFILE);
1092 }
1093 };
1094 let raw = match state.global().file_loader_hook {
1095 Some(load_fn) => load_fn(fname),
1096 None => Err(LuaError::runtime(format_args!(
1097 "no file_loader_hook registered"
1098 ))),
1099 };
1100 let raw = match raw {
1101 Ok(bytes) => bytes,
1102 Err(e) => {
1103 let detail = match e.message_bytes() {
1104 Some(b) => String::from_utf8_lossy(b).into_owned(),
1105 None => format!("{:?}", &e),
1106 };
1107 state.push_fstring(format_args!("cannot open {}: {}", BStr(fname), detail))?;
1108 return Ok(LUA_ERRFILE);
1109 }
1110 };
1111 let payload = skip_bom_and_shebang(&raw);
1112 let mut once = Some(payload);
1113 let boxed: lua_vm::zio::ChunkReader = Box::new(move |_state| Ok(once.take()));
1114 let mut chunkname = b"@".to_vec();
1115 chunkname.extend_from_slice(fname);
1116 let status = lua_vm::api::load(state, boxed, Some(&chunkname), mode)?;
1117 Ok(if status == LuaStatus::Ok {
1118 0
1119 } else {
1120 status as i32
1121 })
1122}
1123
1124pub fn load_bufferx(
1127 state: &mut LuaState,
1128 buff: &[u8],
1129 name: &[u8],
1130 mode: Option<&[u8]>,
1131) -> Result<i32, LuaError> {
1132 let _reader = make_string_reader(buff.to_vec());
1134 let ok = state.load(buff, name, mode)?;
1135 Ok(if ok { 0 } else { 1 })
1136}
1137
1138pub fn load_buffer(state: &mut LuaState, buff: &[u8], name: &[u8]) -> Result<i32, LuaError> {
1141 load_bufferx(state, buff, name, None)
1142}
1143
1144pub fn load_string(state: &mut LuaState, s: &[u8]) -> Result<i32, LuaError> {
1147 load_buffer(state, s, s)
1148}
1149
1150pub fn get_metafield(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<LuaType, LuaError> {
1156 if !state.get_metatable(obj)? {
1157 return Ok(LuaType::Nil);
1158 }
1159 state.push_bytes(event)?;
1160 let tt = state.raw_get(-2)?;
1161 if tt == LuaType::Nil {
1162 state.pop_n(2);
1163 } else {
1164 state.remove(-2)?;
1165 }
1166 Ok(tt)
1167}
1168
1169pub fn call_meta(state: &mut LuaState, obj: i32, event: &[u8]) -> Result<bool, LuaError> {
1173 let obj = state.abs_index(obj);
1174 if get_metafield(state, obj, event)? == LuaType::Nil {
1175 return Ok(false);
1176 }
1177 state.push_value(obj)?;
1178 state.call(1, 1)?;
1179 Ok(true)
1180}
1181
1182pub fn lua_len(state: &mut LuaState, idx: i32) -> Result<i64, LuaError> {
1186 state.len_op(idx)?;
1187 let l = match state.to_integer_x(-1) {
1188 Some(n) => n,
1189 None => {
1190 return Err(LuaError::runtime(format_args!(
1191 "object length is not an integer"
1192 )));
1193 }
1194 };
1195 state.pop_n(1);
1196 Ok(l)
1197}
1198
1199pub fn to_lua_string(state: &mut LuaState, idx: i32) -> Result<Vec<u8>, LuaError> {
1203 let idx = state.abs_index(idx);
1204 if call_meta(state, idx, b"__tostring")? {
1205 if state.type_at(-1) != LuaType::String {
1206 return Err(LuaError::runtime(format_args!(
1207 "'__tostring' must return a string"
1208 )));
1209 }
1210 } else {
1211 match state.type_at(idx) {
1212 LuaType::Number => {
1213 if state.is_integer(idx) {
1214 let i = state.to_integer_x(idx).unwrap_or(0);
1215 state.push_fstring(format_args!("{}", i))?;
1216 } else {
1217 let f = state.to_number_x(idx).unwrap_or(0.0);
1218 state.push_fstring(format_args!("{:?}", f))?;
1219 }
1220 }
1221 LuaType::String => {
1222 state.push_value(idx)?;
1223 }
1224 LuaType::Boolean => {
1225 let b = state.to_boolean(idx);
1226 state.push_string(if b { b"true" } else { b"false" })?;
1227 }
1228 LuaType::Nil => {
1229 state.push_string(b"nil")?;
1230 }
1231 _ => {
1232 let tt = if state.global().lua_version.honors_name_metafield() {
1233 get_metafield(state, idx, b"__name")?
1234 } else {
1235 LuaType::Nil
1236 };
1237 let kind: Vec<u8> = if tt == LuaType::String {
1238 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1239 } else {
1240 state.type_name_at(idx).to_vec()
1241 };
1242 state.push_fstring(format_args!("{}: 0x?", BStr(&kind)))?;
1245 if tt != LuaType::Nil {
1246 state.remove(-2)?;
1247 }
1248 }
1249 }
1250 }
1251 Ok(state.peek_bytes(-1).unwrap_or_default())
1252}
1253
1254pub fn set_funcs(state: &mut LuaState, l: &[LuaReg], nup: i32) -> Result<(), LuaError> {
1258 check_stack(state, nup, Some(b"too many upvalues"))?;
1259 for reg in l {
1260 match reg.func {
1261 None => {
1262 state.push(LuaValue::Bool(false));
1263 }
1264 Some(f) => {
1265 for _ in 0..nup {
1266 state.push_value(-nup)?;
1267 }
1268 state.push_c_closure(f, nup)?;
1269 }
1270 }
1271 state.set_field(-(nup + 2), reg.name)?;
1272 }
1273 state.pop_n(nup as usize);
1274 Ok(())
1275}
1276
1277pub fn get_subtable(state: &mut LuaState, idx: i32, fname: &[u8]) -> Result<bool, LuaError> {
1281 if state.get_field(idx, fname)? == LuaType::Table {
1282 return Ok(true);
1283 }
1284 state.pop_n(1);
1285 let idx = state.abs_index(idx);
1286 let new_tbl = state.new_table();
1287 state.push(LuaValue::Table(new_tbl));
1288 state.push_value(-1)?;
1289 state.set_field(idx, fname)?;
1290 Ok(false)
1291}
1292
1293pub fn requiref(
1298 state: &mut LuaState,
1299 modname: &[u8],
1300 openf: fn(&mut LuaState) -> Result<usize, LuaError>,
1301 glb: bool,
1302) -> Result<(), LuaError> {
1303 get_subtable(state, LUA_REGISTRYINDEX, LUA_LOADED_TABLE)?;
1304 state.get_field(-1, modname)?;
1305 if !state.to_boolean(-1) {
1306 state.pop_n(1);
1307 state.push_c_function(openf)?;
1308 state.push_bytes(modname)?;
1309 state.call(1, 1)?;
1310 state.push_value(-1)?;
1311 state.set_field(-3, modname)?;
1312 }
1313 state.remove(-2)?;
1314 if glb {
1315 state.push_value(-1)?;
1316 state.set_global(modname)?;
1317 }
1318 Ok(())
1319}
1320
1321pub fn get_metatable(state: &mut LuaState, tname: &[u8]) -> Result<LuaType, LuaError> {
1326 state.get_field(LUA_REGISTRYINDEX, tname)
1327}
1328
1329pub fn new_state() -> Result<LuaState, LuaError> {
1335 let _ = default_panic_handler;
1338 let _ = warn_off;
1339 todo!("phase-b: LuaState::new()")
1340}
1341
1342fn default_panic_handler(state: &mut LuaState) -> Result<usize, LuaError> {
1345 let msg = if state.type_at(-1) == LuaType::String {
1346 state.peek_bytes(-1).unwrap_or_else(|| b"?".to_vec())
1347 } else {
1348 b"error object is not a string".to_vec()
1349 };
1350 eprintln!(
1351 "PANIC: unprotected error in call to Lua API ({})",
1352 BStr(&msg)
1353 );
1354 Ok(0) }
1356
1357fn warn_off(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1360 check_control(state, message, tocont)?;
1361 Ok(())
1362}
1363
1364fn warn_on(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1367 if check_control(state, message, tocont)? {
1368 return Ok(());
1369 }
1370 eprint!("Lua warning: ");
1371 warn_cont(state, message, tocont)
1372}
1373
1374fn warn_cont(_state: &mut LuaState, message: &[u8], tocont: bool) -> Result<(), LuaError> {
1377 eprint!("{}", BStr(message));
1378 if tocont {
1380 let _ = (warn_cont as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1381 } else {
1382 eprintln!();
1383 let _ = (warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>,);
1384 }
1385 Ok(())
1386}
1387
1388fn check_control(state: &mut LuaState, message: &[u8], tocont: bool) -> Result<bool, LuaError> {
1392 if tocont || message.first() != Some(&b'@') {
1393 return Ok(false);
1394 }
1395 let cmd = &message[1..];
1396 let _ = state;
1398 if cmd == b"off" {
1399 let _ = warn_off as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1400 } else if cmd == b"on" {
1401 let _ = warn_on as fn(&mut LuaState, &[u8], bool) -> Result<(), LuaError>;
1402 }
1403 Ok(true)
1404}
1405
1406pub fn check_version(state: &mut LuaState, ver: f64, sz: usize) -> Result<(), LuaError> {
1409 const LUAL_NUMSIZES: usize = std::mem::size_of::<i64>() * 16 + std::mem::size_of::<f64>();
1410 if sz != LUAL_NUMSIZES {
1411 return Err(LuaError::runtime(format_args!(
1412 "core and library have incompatible numeric types"
1413 )));
1414 }
1415 let v = state.lua_version();
1416 if (v - ver).abs() > f64::EPSILON {
1417 return Err(LuaError::runtime(format_args!(
1418 "version mismatch: app. needs {}, Lua core provides {}",
1419 ver, v
1420 )));
1421 }
1422 Ok(())
1423}
1424
1425struct BStr<'a>(&'a [u8]);
1433
1434impl<'a> std::fmt::Display for BStr<'a> {
1435 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1436 for &b in self.0 {
1437 if b.is_ascii() {
1438 f.write_char(b as char)?;
1439 } else {
1440 write!(f, "\\x{:02x}", b)?;
1441 }
1442 }
1443 Ok(())
1444 }
1445}
1446
1447use std::fmt::Write as _;
1449
1450