1mod entry;
2mod execute;
3
4pub use entry::{PreCallResult, VmCallFrame};
5pub use execute::VmExecution;
6
7use core::cmp::Ordering;
8use core::ptr;
9
10use luau_common::{ByteSlice, flags};
11
12use crate::Table;
13use crate::call::{CallRuntime, ThreadStack};
14use crate::debug::DebugRuntime;
15use crate::gc::GcBarrier;
16use crate::gc::GcObject;
17use crate::handle::RawHandle;
18use crate::handle::sealed::Sealed;
19use crate::metamethod::{MetamethodRuntime, TmEvent};
20use crate::native::NativeCallContext;
21use crate::number::str_to_num;
22use crate::state::ThreadState;
23use crate::string::{MAX_STRING_SIZE, StringRuntime, TString};
24use crate::table::TableRuntime;
25use crate::thread::Thread;
26use crate::types::{
27 LUA_TBOOLEAN, LUA_TCLASS, LUA_TINTEGER, LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT,
28 LUA_TSTRING, LUA_TTABLE, LUA_TUSERDATA, LUA_TVECTOR, LUA_VECTOR_SIZE,
29};
30use crate::value::{RAW_TVALUE_NIL, TValue, TValueCursor, nil_object};
31use crate::{VmErrorResult, VmResult};
32
33const MAX_TAG_LOOP: i32 = 100;
34
35#[allow(
43 clippy::missing_safety_doc,
44 reason = "all methods share the capability-level safety contract"
45)]
46pub trait VmConversions: Sealed {
47 unsafe fn to_number_internal(&self, value: TValue, result: TValue) -> Option<TValue>;
49
50 unsafe fn to_vector_internal(
52 &self,
53 value: TValue,
54 ) -> Option<[f32; crate::types::LUA_VECTOR_SIZE]>;
55
56 unsafe fn to_string_internal(&self, value: TValue) -> VmErrorResult<i32>;
58}
59
60#[allow(
68 clippy::missing_safety_doc,
69 reason = "all methods share the capability-level safety contract"
70)]
71pub trait VmOperations: Sealed {
72 unsafe fn prepare_forn(
74 &self,
75 limit: TValueCursor,
76 step: TValueCursor,
77 index: TValueCursor,
78 ) -> VmResult;
79
80 unsafe fn call_tm(&self, n_params: i32, result: i32) -> VmResult;
82
83 unsafe fn less_than_internal(&self, left: TValue, right: TValue) -> VmResult<i32>;
85
86 unsafe fn less_equal(&self, left: TValue, right: TValue) -> VmResult<i32>;
88
89 unsafe fn equal_value(&self, left: TValue, right: TValue) -> VmResult<i32>;
91
92 unsafe fn do_arith_impl(
94 &self,
95 result: TValueCursor,
96 left: TValue,
97 right: TValue,
98 op: TmEvent,
99 ) -> VmResult;
100
101 unsafe fn do_len(&self, result: TValueCursor, value: TValue) -> VmResult;
103
104 unsafe fn get_table_internal(
106 &self,
107 table: TValue,
108 key: TValue,
109 value: TValueCursor,
110 ) -> VmResult;
111
112 unsafe fn set_table_internal(&self, table: TValue, key: TValue, value: TValue) -> VmResult;
114
115 unsafe fn concat_internal(&self, total: i32, last: i32) -> VmResult;
117
118 unsafe fn get_import(
120 &self,
121 env: Table,
122 constants: TValueCursor,
123 target: TValueCursor,
124 id: u32,
125 propagate_nil: bool,
126 ) -> VmResult;
127}
128
129pub fn string_compare(left: TString, right: TString) -> i32 {
131 if left == right {
132 return 0;
133 }
134
135 let left_bytes = unsafe { left.as_bytes() };
136 let right_bytes = unsafe { right.as_bytes() };
137
138 let left_first = left_bytes.first().copied().unwrap_or(0);
139 let right_first = right_bytes.first().copied().unwrap_or(0);
140 if left_first != right_first {
141 return i32::from(left_first) - i32::from(right_first);
142 }
143
144 match left_bytes.cmp(right_bytes) {
145 Ordering::Less => -1,
146 Ordering::Equal => 0,
147 Ordering::Greater => 1,
148 }
149}
150
151pub unsafe fn get_comp_tm(
159 thread: &Thread,
160 left: Option<Table>,
161 right: Option<Table>,
162 event: TmEvent,
163) -> Option<TValue> {
164 let left = left?;
165 let tm_left = unsafe { thread.fast_tm(Some(left), event) }?;
166
167 if right == Some(left) {
168 return Some(tm_left);
169 }
170
171 let tm_right = unsafe { thread.fast_tm(right, event) }?;
172 tm_left.raw_equal(tm_right).then_some(tm_left)
173}
174
175impl Thread {
176 unsafe fn call_tm_result(
178 &self,
179 result: TValueCursor,
180 function: TValue,
181 p1: TValue,
182 p2: TValue,
183 ) -> VmResult<TValueCursor> {
184 unsafe {
185 let result_offset = self.save_stack(result);
186 let top = self.stack_top();
187 let top_offset = self.save_stack(top);
188
189 top.value_unchecked().set_obj(function);
190 top.add(1).value_unchecked().set_obj(p1);
191 top.add(2).value_unchecked().set_obj(p2);
192 self.check_stack_internal(3)?;
193
194 let top = self.restore_stack(top_offset);
195 self.set_stack_top(top.add(3));
196 self.call_internal(top, 1)?;
197
198 let result = self.restore_stack(result_offset);
199 let top = self.stack_top().sub(1);
200 self.set_stack_top(top);
201 result.value_unchecked().set_obj(top.value_unchecked());
202 Ok(result)
203 }
204 }
205
206 unsafe fn call_tm3(&self, function: TValue, p1: TValue, p2: TValue, p3: TValue) -> VmResult {
208 unsafe {
209 let top = self.stack_top();
210 let top_offset = self.save_stack(top);
211
212 top.value_unchecked().set_obj(function);
213 top.add(1).value_unchecked().set_obj(p1);
214 top.add(2).value_unchecked().set_obj(p2);
215 top.add(3).value_unchecked().set_obj(p3);
216 self.check_stack_internal(4)?;
217
218 let top = self.restore_stack(top_offset);
219 self.set_stack_top(top.add(4));
220 self.call_internal(top, 0)
221 }
222 }
223
224 unsafe fn call_order_tm(
226 &self,
227 left: TValue,
228 right: TValue,
229 event: TmEvent,
230 error: bool,
231 ) -> VmResult<i32> {
232 unsafe {
233 let Some(tm_left) = self.get_tm_by_obj(left, event) else {
234 if error {
235 return self.order_error(left, right, event).map_err(Into::into);
236 }
237 return Ok(-1);
238 };
239
240 let Some(tm_right) = self.get_tm_by_obj(right, event) else {
241 if error {
242 return self.order_error(left, right, event).map_err(Into::into);
243 }
244 return Ok(-1);
245 };
246
247 if !tm_left.raw_equal(tm_right) {
248 if error {
249 return self.order_error(left, right, event).map_err(Into::into);
250 }
251 return Ok(-1);
252 }
253
254 let result = self.call_tm_result(self.stack_top(), tm_left, left, right)?;
255 Ok(i32::from(!result.value_unchecked().is_false()))
256 }
257 }
258
259 unsafe fn call_bin_tm(
261 &self,
262 left: TValue,
263 right: TValue,
264 result: TValueCursor,
265 event: TmEvent,
266 ) -> VmResult<bool> {
267 unsafe {
268 let tm = self
269 .get_tm_by_obj(left, event)
270 .or_else(|| self.get_tm_by_obj(right, event));
271
272 let Some(tm) = tm else {
273 return Ok(false);
274 };
275
276 self.call_tm_result(result, tm, left, right)?;
277 Ok(true)
278 }
279 }
280
281 unsafe fn coerce_string_slot(&self, object: TValue) -> VmResult<bool> {
283 Ok(object.is_string() || unsafe { self.to_string_internal(object)? != 0 })
284 }
285}
286
287unsafe fn set_vector_result(result: TValueCursor, value: [f32; LUA_VECTOR_SIZE]) {
288 #[cfg(not(feature = "vector4"))]
289 unsafe {
290 result
291 .value_unchecked()
292 .set_vector([value[0], value[1], value[2]]);
293 }
294
295 #[cfg(feature = "vector4")]
296 unsafe {
297 result
298 .value_unchecked()
299 .set_vector([value[0], value[1], value[2], value[3]]);
300 }
301}
302
303impl Thread {
304 unsafe fn call_tm_operation(&self, n_params: i32, result: i32) -> VmResult {
306 unsafe {
307 self.increment_native_call_depth();
308 if self.as_ptr().as_ref().unwrap_unchecked().native_call_depth
309 >= crate::thread::LUAI_MAX_NATIVE_CALLS
310 {
311 self.check_c_stack()?;
312 }
313
314 self.check_stack_internal(crate::thread::LUA_MIN_STACK as i32)?;
315
316 let top = self.stack_top();
317 let function = top.sub(n_params as usize + 1);
318 let closure = function.value_unchecked().closure_value();
319 let call_info = self.incr_ci()?.call_info_unchecked();
320
321 call_info.init_call(
322 function,
323 top.add(crate::thread::LUA_MIN_STACK),
324 i32::from(result >= 0),
325 closure.proto(),
326 );
327
328 debug_assert!(call_info.top() <= self.stack_last());
329
330 debug_assert!(function.value_unchecked().is_function());
331 debug_assert!(closure.is_native());
332
333 self.set_stack_base(function.add(1));
334 debug_assert!(self.stack_top() == self.stack_base().add(n_params as usize));
335
336 let function = closure.native_data().function.unwrap_unchecked();
337 let produced = function(NativeCallContext::new(self))?;
338
339 let parent_cursor = self.current_call_info_cursor().sub(1);
340 let parent = parent_cursor.call_info_unchecked();
341
342 if result >= 0 {
343 let destination = parent.base().add(result as usize);
344 if produced > 0 {
345 let produced_value = self.stack_top().sub(produced);
346 destination
347 .value_unchecked()
348 .set_obj(produced_value.value_unchecked());
349 } else {
350 destination.value_unchecked().set_nil();
351 }
352 }
353
354 self.decrement_native_call_depth();
355 self.restore_call_frame(parent_cursor, parent.top());
356 }
357 Ok(())
358 }
359
360 unsafe fn prepare_forn_operation(
362 &self,
363 limit: TValueCursor,
364 step: TValueCursor,
365 index: TValueCursor,
366 ) -> VmResult {
367 unsafe {
368 if !index.value_unchecked().is_number()
369 && self
370 .to_number_internal(index.value_unchecked(), index.value_unchecked())
371 .is_none()
372 {
373 return self
374 .for_error(index.value_unchecked(), "initial value")
375 .map_err(Into::into);
376 }
377
378 if !limit.value_unchecked().is_number()
379 && self
380 .to_number_internal(limit.value_unchecked(), limit.value_unchecked())
381 .is_none()
382 {
383 return self
384 .for_error(limit.value_unchecked(), "limit")
385 .map_err(Into::into);
386 }
387
388 if !step.value_unchecked().is_number()
389 && self
390 .to_number_internal(step.value_unchecked(), step.value_unchecked())
391 .is_none()
392 {
393 return self
394 .for_error(step.value_unchecked(), "step")
395 .map_err(Into::into);
396 }
397 }
398 Ok(())
399 }
400}
401impl VmConversions for Thread {
402 unsafe fn to_number_internal(&self, value: TValue, result: TValue) -> Option<TValue> {
404 if value.is_number() {
405 return Some(value);
406 }
407
408 if value.is_string() {
409 let string = value.string_value();
410 if let Some(number) = str_to_num(unsafe { string.as_bytes() }) {
411 result.set_number(number);
412 return Some(result);
413 }
414 }
415
416 None
417 }
418
419 unsafe fn to_vector_internal(&self, value: TValue) -> Option<[f32; LUA_VECTOR_SIZE]> {
421 if value.is_vector() {
422 Some(value.vector_value())
423 } else {
424 None
425 }
426 }
427
428 unsafe fn to_string_internal(&self, value: TValue) -> VmErrorResult<i32> {
430 if !value.is_number() {
431 return Ok(0);
432 }
433
434 let mut digits = [0u8; crate::number::LUAI_MAXNUM2STR];
435 let len = crate::number::num_to_str(&mut digits, value.number_value());
436 let string = unsafe { self.intern_string(digits[..len].as_bstr())? };
437 value.set_string_value(string);
438 Ok(1)
439 }
440}
441
442impl VmOperations for Thread {
443 unsafe fn prepare_forn(
444 &self,
445 limit: TValueCursor,
446 step: TValueCursor,
447 index: TValueCursor,
448 ) -> VmResult {
449 unsafe { self.prepare_forn_operation(limit, step, index) }
450 }
451
452 unsafe fn call_tm(&self, n_params: i32, result: i32) -> VmResult {
453 unsafe { self.call_tm_operation(n_params, result) }
454 }
455
456 unsafe fn less_than_internal(&self, left: TValue, right: TValue) -> VmResult<i32> {
458 if left.tt() != right.tt() {
459 unsafe {
460 self.order_error(left, right, TmEvent::Lt)
461 .map_err(Into::into)
462 }
463 } else if left.is_number() {
464 Ok(i32::from(crate::number::num_lt(
465 left.number_value(),
466 right.number_value(),
467 )))
468 } else if left.is_string() {
469 Ok(i32::from(
470 string_compare(left.string_value(), right.string_value()) < 0,
471 ))
472 } else {
473 unsafe { self.call_order_tm(left, right, TmEvent::Lt, true) }
474 }
475 }
476
477 unsafe fn less_equal(&self, left: TValue, right: TValue) -> VmResult<i32> {
479 unsafe {
480 if left.tt() != right.tt() {
481 self.order_error(left, right, TmEvent::Le)
482 .map_err(Into::into)
483 } else if left.is_number() {
484 Ok(i32::from(crate::number::num_le(
485 left.number_value(),
486 right.number_value(),
487 )))
488 } else if left.is_string() {
489 Ok(i32::from(
490 string_compare(left.string_value(), right.string_value()) <= 0,
491 ))
492 } else {
493 let result = self.call_order_tm(left, right, TmEvent::Le, false)?;
494 if result != -1 {
495 Ok(result)
496 } else {
497 let fallback = self.call_order_tm(right, left, TmEvent::Lt, false)?;
498 if fallback == -1 {
499 return self
500 .order_error(left, right, TmEvent::Le)
501 .map_err(Into::into);
502 }
503
504 Ok(i32::from(fallback == 0))
505 }
506 }
507 }
508 }
509
510 unsafe fn equal_value(&self, left: TValue, right: TValue) -> VmResult<i32> {
512 debug_assert_eq!(left.tt(), right.tt());
513
514 unsafe {
515 let tm = match left.tt() {
516 x if x == LUA_TNIL => return Ok(1),
517 x if x == LUA_TNUMBER => {
518 return Ok(i32::from(crate::number::num_eq(
519 left.number_value(),
520 right.number_value(),
521 )));
522 }
523 x if x == LUA_TINTEGER => {
524 return Ok(i32::from(crate::number::int_eq(
525 left.integer_value(),
526 right.integer_value(),
527 )));
528 }
529 x if x == LUA_TVECTOR => {
530 return Ok(i32::from(crate::number::vec_eq(
531 &left.vector_value(),
532 &right.vector_value(),
533 )));
534 }
535 x if x == LUA_TBOOLEAN => {
536 return Ok(i32::from(left.boolean_value() == right.boolean_value()));
537 }
538 x if x == LUA_TLIGHTUSERDATA => {
539 return Ok(i32::from(
540 left.pointer_value() == right.pointer_value()
541 && left.light_userdata_tag() == right.light_userdata_tag(),
542 ));
543 }
544 x if x == LUA_TUSERDATA => {
545 let left_userdata = left.userdata_value();
546 let right_userdata = right.userdata_value();
547 let tm = get_comp_tm(
548 self,
549 left_userdata.metatable(),
550 right_userdata.metatable(),
551 TmEvent::Eq,
552 );
553
554 if tm.is_none() {
555 return Ok(i32::from(left_userdata == right_userdata));
556 }
557
558 tm.unwrap_unchecked()
559 }
560 x if x == LUA_TCLASS => {
561 return Ok(i32::from(left.class_value() == right.class_value()));
562 }
563 x if x == LUA_TOBJECT => {
564 let left_object = left.object_value();
565 let right_object = right.object_value();
566
567 if left_object.class() != right_object.class() {
568 return Ok(0);
569 }
570
571 let Some(tm) = self.get_tm_by_obj(left, TmEvent::Eq) else {
572 return Ok(i32::from(left_object == right_object));
573 };
574
575 tm
576 }
577 x if x == LUA_TTABLE => {
578 let left_table = left.table_value();
579 let right_table = right.table_value();
580 let tm = get_comp_tm(
581 self,
582 left_table.metatable(),
583 right_table.metatable(),
584 TmEvent::Eq,
585 );
586
587 if tm.is_none() {
588 return Ok(i32::from(left_table == right_table));
589 }
590
591 tm.unwrap_unchecked()
592 }
593 _ => return Ok(i32::from(left.gc_value() == right.gc_value())),
594 };
595
596 let result = self.call_tm_result(self.stack_top(), tm, left, right)?;
597 Ok(i32::from(!result.value_unchecked().is_false()))
598 }
599 }
600
601 unsafe fn do_arith_impl(
603 &self,
604 result: TValueCursor,
605 left: TValue,
606 right: TValue,
607 op: TmEvent,
608 ) -> VmResult {
609 let mut left_number_storage = RAW_TVALUE_NIL;
610 let mut right_number_storage = RAW_TVALUE_NIL;
611 unsafe {
612 let (left_number, right_number) = (
613 TValue::from_mut(&mut left_number_storage),
614 TValue::from_mut(&mut right_number_storage),
615 );
616
617 let left_vector = left.is_vector().then(|| left.vector_value());
618 let right_vector = right.is_vector().then(|| right.vector_value());
619
620 if let (Some(left_vector), Some(right_vector)) = (left_vector, right_vector) {
621 match op {
622 TmEvent::Add | TmEvent::Sub | TmEvent::Mul | TmEvent::Div | TmEvent::IDiv => {
623 let mut value = [0.0; LUA_VECTOR_SIZE];
624 for index in 0..LUA_VECTOR_SIZE {
625 value[index] = match op {
626 TmEvent::Add => left_vector[index] + right_vector[index],
627 TmEvent::Sub => left_vector[index] - right_vector[index],
628 TmEvent::Mul => left_vector[index] * right_vector[index],
629 TmEvent::Div => left_vector[index] / right_vector[index],
630 TmEvent::IDiv => crate::number::num_idiv(
631 left_vector[index] as f64,
632 right_vector[index] as f64,
633 ) as f32,
634 _ => unreachable!(),
635 };
636 }
637 set_vector_result(result, value);
638 return Ok(());
639 }
640 TmEvent::Unm => {
641 let mut value = [0.0; LUA_VECTOR_SIZE];
642 for index in 0..LUA_VECTOR_SIZE {
643 value[index] = -left_vector[index];
644 }
645 set_vector_result(result, value);
646 return Ok(());
647 }
648 _ => {}
649 }
650 } else if let Some(left_vector) = left_vector {
651 if let Some(right_number) = self
652 .to_number_internal(right, right_number)
653 .map(|n| n.number_value() as f32)
654 {
655 match op {
656 TmEvent::Mul | TmEvent::Div | TmEvent::IDiv => {
657 let mut value = [0.0; LUA_VECTOR_SIZE];
658 for index in 0..LUA_VECTOR_SIZE {
659 value[index] = match op {
660 TmEvent::Mul => left_vector[index] * right_number,
661 TmEvent::Div => left_vector[index] / right_number,
662 TmEvent::IDiv => crate::number::num_idiv(
663 left_vector[index] as f64,
664 right_number as f64,
665 ) as f32,
666 _ => unreachable!(),
667 };
668 }
669 set_vector_result(result, value);
670 return Ok(());
671 }
672 _ => {}
673 }
674 }
675 } else if let Some(right_vector) = right_vector
676 && let Some(left_number) = self
677 .to_number_internal(left, left_number)
678 .map(|n| n.number_value() as f32)
679 {
680 match op {
681 TmEvent::Mul | TmEvent::Div | TmEvent::IDiv => {
682 let mut value = [0.0; LUA_VECTOR_SIZE];
683 for index in 0..LUA_VECTOR_SIZE {
684 value[index] = match op {
685 TmEvent::Mul => left_number * right_vector[index],
686 TmEvent::Div => left_number / right_vector[index],
687 TmEvent::IDiv => crate::number::num_idiv(
688 left_number as f64,
689 right_vector[index] as f64,
690 ) as f32,
691 _ => unreachable!(),
692 };
693 }
694 set_vector_result(result, value);
695 return Ok(());
696 }
697 _ => {}
698 }
699 }
700
701 if let (Some(left_number), Some(right_number)) = (
702 self.to_number_internal(left, left_number)
703 .map(|n| n.number_value()),
704 self.to_number_internal(right, right_number)
705 .map(|n| n.number_value()),
706 ) {
707 result.value_unchecked().set_number(match op {
708 TmEvent::Add => crate::number::num_add(left_number, right_number),
709 TmEvent::Sub => crate::number::num_sub(left_number, right_number),
710 TmEvent::Mul => crate::number::num_mul(left_number, right_number),
711 TmEvent::Div => crate::number::num_div(left_number, right_number),
712 TmEvent::IDiv => crate::number::num_idiv(left_number, right_number),
713 TmEvent::Mod => crate::number::num_mod(left_number, right_number),
714 TmEvent::Pow => crate::number::num_pow(left_number, right_number),
715 TmEvent::Unm => crate::number::num_unm(left_number),
716 _ => unreachable!("invalid arithmetic metamethod {:?}", op),
717 });
718 return Ok(());
719 }
720
721 if !self.call_bin_tm(left, right, result, op)? {
722 return self.arith_error(left, right, op).map_err(Into::into);
723 }
724 }
725 Ok(())
726 }
727
728 unsafe fn do_len(&self, result: TValueCursor, value: TValue) -> VmResult {
730 unsafe {
731 let tm = match value.tt() {
732 x if x == LUA_TTABLE => {
733 let table = value.table_value();
734 match self.fast_tm(table.metatable(), TmEvent::Len) {
735 Some(tm) => tm,
736 None => {
737 result.value_unchecked().set_number(table.getn() as f64);
738 return Ok(());
739 }
740 }
741 }
742 x if x == LUA_TSTRING => {
743 result.value_unchecked().set_number(
744 value
745 .string_value()
746 .as_ptr()
747 .as_ref()
748 .unwrap_unchecked()
749 .len as f64,
750 );
751 return Ok(());
752 }
753 _ => match self.get_tm_by_obj(value, TmEvent::Len) {
754 Some(tm) => tm,
755 None => return self.type_error(value, "get length of").map_err(Into::into),
756 },
757 };
758
759 let result = self.call_tm_result(result, tm, value, nil_object())?;
760 if !result.value_unchecked().is_number() {
761 return crate::run_error!(self, "'__len' must return a number").map_err(Into::into);
762 }
763 }
764 Ok(())
765 }
766
767 unsafe fn get_table_internal(
769 &self,
770 table: TValue,
771 key: TValue,
772 value: TValueCursor,
773 ) -> VmResult {
774 let mut table = table;
775
776 unsafe {
777 for _ in 0..MAX_TAG_LOOP {
778 let tm;
779 if table.is_table() {
780 let hash = table.table_value();
781 let result = hash.get(key);
782
783 if result != nil_object() {
784 self.set_cached_slot(hash.value_slot_unchecked(result));
785 }
786
787 if !result.is_nil() || {
788 tm = self.fast_tm(hash.metatable(), TmEvent::Index);
789 tm.is_none()
790 } {
791 value.value_unchecked().set_obj(result);
792 return Ok(());
793 }
794 } else if flags::DebugLuauUserDefinedClassesRuntime.get() && table.is_object() {
795 let instance = table.object_value();
796 let Some(member) = instance.lookup_member(key) else {
797 return self.missing_member_error(table, key).map_err(Into::into);
798 };
799 value.value_unchecked().set_obj(member);
800 return Ok(());
801 } else if flags::DebugLuauUserDefinedClassesRuntime.get() && table.is_class() {
802 let class = table.class_value();
803 let Some(static_member) = class.lookup_static_member(key) else {
804 return self.missing_member_error(table, key).map_err(Into::into);
805 };
806 value.value_unchecked().set_obj(static_member);
807 return Ok(());
808 } else {
809 let Some(candidate) = self.get_tm_by_obj(table, TmEvent::Index) else {
810 return self.index_error(table, key).map_err(Into::into);
811 };
812 tm = Some(candidate);
813 }
814
815 let tm = tm.unwrap_unchecked();
816 if tm.is_function() {
817 self.call_tm_result(value, tm, table, key)?;
818 return Ok(());
819 }
820
821 table = tm;
822 }
823
824 crate::run_error!(self, "'__index' chain too long; possible loop").map_err(Into::into)
825 }
826 }
827
828 unsafe fn set_table_internal(&self, table: TValue, key: TValue, value: TValue) -> VmResult {
830 let mut temp_storage = RAW_TVALUE_NIL;
831 let temp = unsafe { TValue::from_mut(&mut temp_storage) };
832 let mut table = table;
833
834 unsafe {
835 for _ in 0..MAX_TAG_LOOP {
836 let tm;
837 if table.is_table() {
838 let hash = table.table_value();
839 let old_value = hash.get(key);
840
841 if !old_value.is_nil() || {
842 tm = self.fast_tm(hash.metatable(), TmEvent::NewIndex);
843 tm.is_none()
844 } {
845 if hash.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
846 return self.readonly_error().map_err(Into::into);
847 }
848
849 let new_value = self.set_slot(hash, old_value, key)?;
850 self.set_cached_slot(hash.value_slot_unchecked(new_value));
851 new_value.set_obj(value);
852
853 if value.is_collectable() {
854 let table_object: GcObject = hash.into();
855 let child = value.gc_value();
856 if table_object.is_black() && child.is_white() {
857 self.barrier_table(hash, child);
858 }
859 }
860 return Ok(());
861 }
862 } else if flags::DebugLuauUserDefinedClassesRuntime.get() && table.is_object() {
863 let instance = table.object_value();
864 let class = instance.class();
865 let Some(offset) = class.member_offset(key) else {
866 return self.missing_member_error(table, key).map_err(Into::into);
867 };
868 debug_assert!(instance.offset_in_bounds(offset));
869 if offset
870 >= class
871 .as_ptr()
872 .as_ref()
873 .unwrap_unchecked()
874 .number_of_instance_members
875 {
876 return self.index_error(table, key).map_err(Into::into);
877 }
878
879 let member = instance.lookup_member_at_offset(offset);
880 member.set_obj(value);
881
882 if value.is_collectable() {
883 let object: GcObject = instance.into();
884 let child = value.gc_value();
885 if object.is_black() && child.is_white() {
886 self.barrier_forward(object, child);
887 }
888 }
889 return Ok(());
890 } else {
891 let Some(candidate) = self.get_tm_by_obj(table, TmEvent::NewIndex) else {
892 return self.index_error(table, key).map_err(Into::into);
893 };
894 tm = Some(candidate);
895 }
896
897 let tm = tm.unwrap_unchecked();
898 if tm.is_function() {
899 self.call_tm3(tm, table, key, value)?;
900 return Ok(());
901 }
902
903 temp.set_obj(tm);
904 table = temp;
905 }
906
907 crate::run_error!(self, "'__newindex' chain too long; possible loop")
908 .map_err(Into::into)
909 }
910 }
911
912 unsafe fn concat_internal(&self, mut total: i32, mut last: i32) -> VmResult {
914 unsafe {
915 while total > 1 {
916 let top = self.stack_base().add(last as usize + 1);
917 let mut count = 2;
918
919 if !(top.sub(2).value_unchecked().is_string()
920 || top.sub(2).value_unchecked().is_number())
921 || !self.coerce_string_slot(top.sub(1).value_unchecked())?
922 {
923 if !self.call_bin_tm(
924 top.sub(2).value_unchecked(),
925 top.sub(1).value_unchecked(),
926 top.sub(2),
927 TmEvent::Concat,
928 )? {
929 return self
930 .concat_error(
931 top.sub(2).value_unchecked(),
932 top.sub(1).value_unchecked(),
933 )
934 .map_err(Into::into);
935 }
936 } else if top
937 .sub(1)
938 .value_unchecked()
939 .string_value()
940 .as_ptr()
941 .as_ref()
942 .unwrap_unchecked()
943 .len
944 == 0
945 {
946 let _ = self.coerce_string_slot(top.sub(2).value_unchecked())?;
947 } else {
948 let mut total_len = top
949 .sub(1)
950 .value_unchecked()
951 .string_value()
952 .as_ptr()
953 .as_ref()
954 .unwrap_unchecked()
955 .len as usize;
956
957 count = 1;
958 while count < total {
959 let slot = top.sub(count as usize + 1);
960 if !self.coerce_string_slot(slot.value_unchecked())? {
961 break;
962 }
963
964 let len = slot
965 .value_unchecked()
966 .string_value()
967 .as_ptr()
968 .as_ref()
969 .unwrap_unchecked()
970 .len as usize;
971 if len > MAX_STRING_SIZE - total_len {
972 return crate::run_error!(self, "string length overflow")
973 .map_err(Into::into);
974 }
975 total_len += len;
976 count += 1;
977 }
978
979 let mut local_buf = [0u8; crate::thread::LUA_BUFFER_SIZE];
980 let (buffer, heap_string) = if total_len < crate::thread::LUA_BUFFER_SIZE {
981 (local_buf.as_mut_ptr(), None)
982 } else {
983 let string = self.buffer_start(total_len)?;
984 (string.data_mut_ptr(), Some(string))
985 };
986
987 let mut write = 0usize;
988 for index in (1..=count).rev() {
989 let string = top.sub(index as usize).value_unchecked().string_value();
990 let bytes = string.as_bytes();
991 ptr::copy_nonoverlapping(bytes.as_ptr(), buffer.add(write), bytes.len());
992 write += bytes.len();
993 }
994
995 let dest = top.sub(count as usize);
996 let result = if let Some(string) = heap_string {
997 self.buffer_finish(string)?
998 } else {
999 self.intern_string(
1000 core::slice::from_raw_parts(buffer, total_len).as_bstr(),
1001 )?
1002 };
1003
1004 dest.value_unchecked().set_string_value(result);
1005 }
1006
1007 total -= count - 1;
1008 last -= count - 1;
1009 }
1010 }
1011 Ok(())
1012 }
1013
1014 unsafe fn get_import(
1016 &self,
1017 env: Table,
1018 constants: TValueCursor,
1019 target: TValueCursor,
1020 id: u32,
1021 propagate_nil: bool,
1022 ) -> VmResult {
1023 let count = (id >> 30) as i32;
1024 debug_assert!(count > 0);
1025
1026 let id0 = ((id >> 20) & 1023) as usize;
1027 let id1 = ((id >> 10) & 1023) as usize;
1028 let id2 = (id & 1023) as usize;
1029
1030 let mut global_storage = RAW_TVALUE_NIL;
1031 unsafe {
1032 let result_offset = self.save_stack(target);
1033 let global = TValue::from_mut(&mut global_storage);
1034 global.set_table_value(env);
1035 self.get_table_internal(global, constants.add(id0).value_unchecked(), target)?;
1036
1037 if count < 2 {
1038 return Ok(());
1039 }
1040
1041 let target = self.restore_stack(result_offset);
1042 if !propagate_nil || !target.value_unchecked().is_nil() {
1043 self.get_table_internal(
1044 target.value_unchecked(),
1045 constants.add(id1).value_unchecked(),
1046 target,
1047 )?;
1048 }
1049
1050 if count < 3 {
1051 return Ok(());
1052 }
1053
1054 let target = self.restore_stack(result_offset);
1055 if !propagate_nil || !target.value_unchecked().is_nil() {
1056 self.get_table_internal(
1057 target.value_unchecked(),
1058 constants.add(id2).value_unchecked(),
1059 target,
1060 )?;
1061 }
1062 }
1063 Ok(())
1064 }
1065}