1use core::mem::MaybeUninit;
2use core::ptr::{self, NonNull};
3
4use luau_common::{ByteSlice, flags};
5
6use super::stack::RawStackAccess;
7use super::{LUA_GLOBALS_INDEX, LUA_NOREF, LUA_REFNIL, LUA_REGISTRY_INDEX, Thread};
8use crate::debug::DebugRuntime;
9use crate::gc::{GcBarrier, GcRuntime};
10use crate::handle::RawHandle;
11use crate::native::{NativeFunction, NativeModule};
12use crate::state::ThreadState;
13use crate::string::StringRuntime;
14use crate::table::TableRuntime;
15use crate::types::{LUA_TFUNCTION, LUA_TNIL, LUA_TOBJECT, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA};
16use crate::value::{RAW_TVALUE_NIL, TValue};
17use crate::vm::VmOperations;
18use crate::{VmErrorResult, VmResult};
19
20impl Thread {
22 pub unsafe fn create_table(&self, array_size: usize, record_size: usize) -> VmErrorResult {
24 let (Ok(array_size), Ok(record_size)) =
25 (i32::try_from(array_size), i32::try_from(record_size))
26 else {
27 return unsafe { crate::run_error!(self, "table overflow") };
28 };
29
30 unsafe {
31 self.check_gc()?;
32 self.thread_barrier();
33 self.ensure_stack(self, 1)?;
34
35 let table = self.new_table_internal(array_size, record_size)?;
36 let top = self.stack_top();
37 top.value_unchecked().set_table_value(table);
38 debug_assert!(top < self.current_call_info().top());
39 self.set_stack_top(top.add(1));
40 }
41 Ok(())
42 }
43
44 pub unsafe fn new_table(&self) -> VmErrorResult {
46 unsafe { self.create_table(0, 0) }
47 }
48
49 pub unsafe fn get_table(&self, index: i32) -> VmResult<i32> {
51 unsafe {
52 let top = self.stack_top().sub(1);
53 debug_assert!(top >= self.stack_base());
54
55 self.thread_barrier();
56
57 let table = self.to_object(index).unwrap_unchecked();
58 self.get_table_internal(table, top.value_unchecked(), top)?;
59 Ok(self.stack_top().sub(1).value_unchecked().tt())
60 }
61 }
62
63 pub unsafe fn get_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmResult<i32> {
65 let key = key.as_ref();
66 let mut key_storage = RAW_TVALUE_NIL;
67 unsafe {
68 self.thread_barrier();
69 self.ensure_stack(self, 1)?;
70
71 let table = self.to_object(index).unwrap_unchecked();
72 let mut table_storage = MaybeUninit::uninit();
73 ptr::copy_nonoverlapping(table.as_ptr(), table_storage.as_mut_ptr(), 1);
74 let table = TValue::from_raw(NonNull::new_unchecked(table_storage.as_mut_ptr()));
75 let key_value = TValue::from_mut(&mut key_storage);
76 key_value.set_string_value(self.intern_string(key.as_bstr())?);
77 let top = self.stack_top();
78 self.get_table_internal(table, key_value, top)?;
79 let top = self.stack_top();
80 debug_assert!(top < self.current_call_info().top());
81 self.set_stack_top(top.add(1));
82 Ok(top.value_unchecked().tt())
83 }
84 }
85
86 pub unsafe fn get_global(&self, name: impl AsRef<[u8]>) -> VmResult<i32> {
88 unsafe { self.get_field(LUA_GLOBALS_INDEX, name) }
89 }
90
91 pub unsafe fn raw_get_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmErrorResult<i32> {
93 let key = key.as_ref();
94 unsafe {
95 self.thread_barrier();
96 self.ensure_stack(self, 1)?;
97
98 let object = self.to_object(index).unwrap_unchecked();
99 debug_assert!(object.is_table());
100 let table = object.table_value();
101 let string = self.intern_string(key.as_bstr())?;
102 let top = self.stack_top();
103 top.value_unchecked().set_obj(table.get_str(string));
104 debug_assert!(top < self.current_call_info().top());
105 self.set_stack_top(top.add(1));
106 Ok(top.value_unchecked().tt())
107 }
108 }
109
110 pub unsafe fn raw_get(&self, index: i32) -> i32 {
112 unsafe {
113 self.thread_barrier();
114
115 let table = self.to_object(index).unwrap_unchecked();
116 debug_assert!(table.is_table());
117
118 let top = self.stack_top().sub(1);
119 top.value_unchecked()
120 .set_obj(table.table_value().get(top.value_unchecked()));
121 top.value_unchecked().tt()
122 }
123 }
124
125 pub unsafe fn raw_geti(&self, index: i32, key: i32) -> VmErrorResult<i32> {
127 unsafe {
128 self.thread_barrier();
129 self.ensure_stack(self, 1)?;
130
131 let table = self.to_object(index).unwrap_unchecked();
132 debug_assert!(table.is_table());
133
134 let top = self.stack_top();
135 top.value_unchecked()
136 .set_obj(table.table_value().get_num(key));
137 debug_assert!(top < self.current_call_info().top());
138 self.set_stack_top(top.add(1));
139 Ok(top.value_unchecked().tt())
140 }
141 }
142
143 pub unsafe fn raw_getp_tagged(&self, index: i32, key: *mut (), tag: i32) -> VmErrorResult<i32> {
145 unsafe {
146 self.thread_barrier();
147 self.ensure_stack(self, 1)?;
148
149 let table = self.to_object(index).unwrap_unchecked();
150 debug_assert!(table.is_table());
151
152 let top = self.stack_top();
153 top.value_unchecked()
154 .set_obj(table.table_value().getp(key, tag));
155 debug_assert!(top < self.current_call_info().top());
156 self.set_stack_top(top.add(1));
157 Ok(top.value_unchecked().tt())
158 }
159 }
160
161 pub unsafe fn raw_getp(&self, index: i32, key: *mut ()) -> VmErrorResult<i32> {
163 unsafe { self.raw_getp_tagged(index, key, 0) }
164 }
165
166 pub unsafe fn set_readonly(&self, index: i32, enabled: i32) {
168 unsafe {
169 let object = self.to_object(index).unwrap_unchecked();
170 debug_assert!(object.is_table());
171
172 let table = object.table_value();
173 let registry = self.global().registry().table_value();
174 debug_assert!(table != registry);
175 table.as_ptr().as_mut().unwrap_unchecked().readonly = u8::from(enabled != 0);
176 }
177 }
178
179 pub unsafe fn get_readonly(&self, index: i32) -> i32 {
181 unsafe {
182 let object = self.to_object(index).unwrap_unchecked();
183 debug_assert!(object.is_table());
184 i32::from(
185 object
186 .table_value()
187 .as_ptr()
188 .as_ref()
189 .unwrap_unchecked()
190 .readonly
191 != 0,
192 )
193 }
194 }
195
196 pub unsafe fn set_safe_env(&self, index: i32, enabled: i32) {
198 let object = unsafe { self.to_object(index).unwrap_unchecked() };
199 debug_assert!(object.is_table());
200
201 unsafe {
202 object
203 .table_value()
204 .as_ptr()
205 .as_mut()
206 .unwrap_unchecked()
207 .safe_env = u8::from(enabled != 0);
208 }
209 }
210
211 pub unsafe fn get_metatable(&self, index: i32) -> VmErrorResult<i32> {
213 unsafe {
214 self.thread_barrier();
215 self.ensure_stack(self, 1)?;
216
217 let object = self.to_object(index);
218 let metatable = if let Some(object) = object {
219 match object.tt() {
220 x if x == LUA_TTABLE => object.table_value().metatable(),
221 x if x == LUA_TUSERDATA => object.userdata_value().metatable(),
222 x if x == LUA_TOBJECT => object.object_value().class().instance_metatable(),
223 tag => self.global().metatable(tag as usize),
224 }
225 } else {
226 None
227 };
228
229 if let Some(metatable) = metatable {
230 let top = self.stack_top();
231 top.value_unchecked().set_table_value(metatable);
232 debug_assert!(top < self.current_call_info().top());
233 self.set_stack_top(top.add(1));
234 Ok(1)
235 } else {
236 Ok(0)
237 }
238 }
239 }
240
241 pub unsafe fn get_fenv(&self, index: i32) -> VmErrorResult {
243 unsafe {
244 self.thread_barrier();
245 self.ensure_stack(self, 1)?;
246
247 let object = self.to_object(index).unwrap_unchecked();
248 let top = self.stack_top();
249 match object.tt() {
250 x if x == LUA_TFUNCTION => top
251 .value_unchecked()
252 .set_table_value(object.closure_value().env()),
253 x if x == LUA_TTHREAD => top
254 .value_unchecked()
255 .set_table_value(object.thread_value().globals()),
256 _ => top.value_unchecked().set_nil(),
257 }
258
259 debug_assert!(top < self.current_call_info().top());
260 self.set_stack_top(top.add(1));
261 }
262 Ok(())
263 }
264
265 pub unsafe fn set_table(&self, index: i32) -> VmResult {
267 unsafe {
268 let top = self.stack_top();
269 debug_assert!(top.offset_from(self.stack_base()) >= 2);
270
271 let table = self.to_object(index).unwrap_unchecked();
272 let key = top.sub(2).value_unchecked();
273 let value = top.sub(1).value_unchecked();
274 self.set_table_internal(table, key, value)?;
275 self.set_stack_top(self.stack_top().sub(2));
276 }
277 Ok(())
278 }
279
280 pub unsafe fn set_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmResult {
282 let key = key.as_ref();
283 let mut key_storage = RAW_TVALUE_NIL;
284 unsafe {
285 let top = self.stack_top();
286 debug_assert!(top.offset_from(self.stack_base()) > 0);
287
288 let table = self.to_object(index).unwrap_unchecked();
289 let mut table_storage = MaybeUninit::uninit();
290 ptr::copy_nonoverlapping(table.as_ptr(), table_storage.as_mut_ptr(), 1);
291 let table = TValue::from_raw(NonNull::new_unchecked(table_storage.as_mut_ptr()));
292 let key_value = TValue::from_mut(&mut key_storage);
293 key_value.set_string_value(self.intern_string(key.as_bstr())?);
294 let value = top.sub(1).value_unchecked();
295
296 self.set_table_internal(table, key_value, value)?;
297 self.set_stack_top(self.stack_top().sub(1));
298 }
299 Ok(())
300 }
301
302 pub unsafe fn raw_set_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmErrorResult {
304 let key = key.as_ref();
305 unsafe {
306 let top = self.stack_top();
307 debug_assert!(top.offset_from(self.stack_base()) > 0);
308
309 let object = self.to_object(index).unwrap_unchecked();
310 debug_assert!(object.is_table());
311 let table = object.table_value();
312 let value = top.sub(1).value_unchecked();
313
314 if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
315 return self.readonly_error();
316 }
317
318 let string = self.intern_string(key.as_bstr())?;
319 let node_cursor = self.set_str(table, string)?;
320 node_cursor
321 .node_unchecked()
322 .value_unchecked()
323 .set_obj(value);
324 if value.is_collectable() {
325 self.barrier_table(table, value.gc_value());
326 }
327
328 self.set_stack_top(top.sub(1));
329 }
330 Ok(())
331 }
332
333 pub unsafe fn set_global(&self, name: impl AsRef<[u8]>) -> VmErrorResult {
335 unsafe { self.raw_set_field(LUA_GLOBALS_INDEX, name) }
336 }
337
338 pub unsafe fn concat(&self, count: i32) -> VmResult {
340 debug_assert!(count >= 0);
341
342 if count >= 2 {
343 unsafe {
344 self.check_gc()?;
345 self.thread_barrier();
346 self.concat_internal(count, self.get_top() - 1)?;
347 self.set_stack_top(self.stack_top().sub((count - 1) as usize));
348 }
349 } else if count == 0 {
350 unsafe {
351 self.thread_barrier();
352 self.ensure_stack(self, 1)?;
353 self.push_string("")?;
354 }
355 }
356 Ok(())
357 }
358
359 pub unsafe fn raw_set(&self, index: i32) -> VmErrorResult {
361 unsafe {
362 let top = self.stack_top();
363 debug_assert!(top.offset_from(self.stack_base()) >= 2);
364
365 let object = self.to_object(index).unwrap_unchecked();
366 debug_assert!(object.is_table());
367 let table = object.table_value();
368 let key = top.sub(2).value_unchecked();
369 let value = top.sub(1).value_unchecked();
370
371 if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
372 return self.readonly_error();
373 }
374
375 let slot = self.set(table, key)?;
376 slot.set_obj(value);
377 if value.is_collectable() {
378 self.barrier_table(table, value.gc_value());
379 }
380
381 self.set_stack_top(top.sub(2));
382 }
383 Ok(())
384 }
385
386 pub unsafe fn raw_seti(&self, index: i32, key: i32) -> VmErrorResult {
388 unsafe {
389 let top = self.stack_top();
390 debug_assert!(top.offset_from(self.stack_base()) > 0);
391
392 let object = self.to_object(index).unwrap_unchecked();
393 debug_assert!(object.is_table());
394 let table = object.table_value();
395 let value = top.sub(1).value_unchecked();
396
397 if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
398 return self.readonly_error();
399 }
400
401 let slot = self.set_num(table, key)?;
402 slot.set_obj(value);
403 if value.is_collectable() {
404 self.barrier_table(table, value.gc_value());
405 }
406
407 self.set_stack_top(top.sub(1));
408 }
409 Ok(())
410 }
411
412 pub unsafe fn raw_setp_tagged(&self, index: i32, key: *mut (), tag: i32) -> VmErrorResult {
414 unsafe {
415 let top = self.stack_top();
416 debug_assert!(top.offset_from(self.stack_base()) > 0);
417
418 let object = self.to_object(index).unwrap_unchecked();
419 debug_assert!(object.is_table());
420 let table = object.table_value();
421 let value = top.sub(1).value_unchecked();
422
423 if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
424 return self.readonly_error();
425 }
426
427 let slot = self.setp(table, key, tag)?;
428 slot.set_obj(value);
429 if value.is_collectable() {
430 self.barrier_table(table, value.gc_value());
431 }
432
433 self.set_stack_top(top.sub(1));
434 }
435 Ok(())
436 }
437
438 pub unsafe fn raw_setp(&self, index: i32, key: *mut ()) -> VmErrorResult {
440 unsafe { self.raw_setp_tagged(index, key, 0) }
441 }
442
443 pub unsafe fn set_metatable(&self, index: i32) -> VmErrorResult<i32> {
445 unsafe {
446 let top = self.stack_top();
447 debug_assert!(top.offset_from(self.stack_base()) > 0);
448
449 let object = self.to_object(index).unwrap_unchecked();
450 let metatable_value = top.sub(1).value_unchecked();
451
452 let metatable = if metatable_value.is_nil() {
453 None
454 } else {
455 debug_assert!(metatable_value.is_table());
456 Some(metatable_value.table_value())
457 };
458
459 match object.tt() {
460 x if x == LUA_TTABLE => {
461 let table = object.table_value();
462 if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
463 return self.readonly_error();
464 }
465
466 table.set_metatable(metatable);
467 if let Some(metatable) = metatable {
468 self.object_barrier(table.into(), metatable.into());
469 }
470 }
471 x if x == LUA_TUSERDATA => {
472 let userdata = object.userdata_value();
473 userdata.set_metatable(metatable);
474 if let Some(metatable) = metatable {
475 self.object_barrier(userdata.into(), metatable.into());
476 }
477 }
478 tag => {
479 self.global().as_ptr().as_mut().unwrap_unchecked().mt[tag as usize] =
480 metatable.map_or(ptr::null_mut(), |table| table.as_ptr());
481 }
482 }
483
484 self.set_stack_top(top.sub(1));
485 Ok(1)
486 }
487 }
488
489 pub unsafe fn set_fenv(&self, index: i32) -> i32 {
491 unsafe {
492 let top = self.stack_top();
493 debug_assert!(top.offset_from(self.stack_base()) > 0);
494 let object = self.to_object(index).unwrap_unchecked();
495 let environment = top.sub(1).value_unchecked();
496 debug_assert!(environment.is_table());
497
498 let environment = environment.table_value();
499 let result = match object.tt() {
500 x if x == LUA_TFUNCTION => {
501 let closure = object.closure_value();
502 closure.set_env(environment);
503 self.object_barrier(closure.into(), environment.into());
504 1
505 }
506 x if x == LUA_TTHREAD => {
507 let thread = object.thread_value();
508 thread.set_globals(environment);
509 self.object_barrier((&thread).into(), environment.into());
510 1
511 }
512 _ => 0,
513 };
514
515 self.set_stack_top(top.sub(1));
516 result
517 }
518 }
519
520 pub unsafe fn next(&self, index: i32) -> VmErrorResult<i32> {
522 unsafe {
523 let top = self.stack_top();
524 debug_assert!(top.offset_from(self.stack_base()) > 0);
525 self.thread_barrier();
526 self.ensure_stack(self, 1)?;
527
528 let table = self.to_object(index).unwrap_unchecked();
529 debug_assert!(table.is_table());
530 let key = top.sub(1);
531
532 let more = self.next_internal(table.table_value(), key)?;
533 if more != 0 {
534 debug_assert!(top < self.current_call_info().top());
535 self.set_stack_top(top.add(1));
536 } else {
537 self.set_stack_top(top.sub(1));
538 }
539
540 Ok(more)
541 }
542 }
543
544 pub unsafe fn raw_iter(&self, index: i32, mut iter: i32) -> VmErrorResult<i32> {
546 unsafe {
547 self.thread_barrier();
548 self.ensure_stack(self, 2)?;
549
550 let table = self.to_object(index).unwrap_unchecked();
551 debug_assert!(table.is_table());
552 debug_assert!(iter >= 0);
553
554 let table = table.table_value();
555 let size_array = table.as_ptr().as_ref().unwrap_unchecked().size_array;
556
557 while (iter as u32) < size_array as u32 {
558 let entry = table.array_slot(iter as usize);
559 if !entry.is_nil() {
560 let top = self.stack_top();
561 top.value_unchecked().set_number((iter + 1) as f64);
562 top.add(1).value_unchecked().set_obj(entry);
563 debug_assert!(top.add(2) <= self.current_call_info().top());
564 self.set_stack_top(top.add(2));
565 return Ok(iter + 1);
566 }
567 iter += 1;
568 }
569
570 let size_node = table.node_count();
571 while ((iter - size_array) as u32) < size_node as u32 {
572 let node = table.node(iter - size_array);
573 if !node.value_unchecked().is_nil() {
574 let top = self.stack_top();
575 node.write_key_to_value(top.value_unchecked());
576 top.add(1).value_unchecked().set_obj(node.value_unchecked());
577 debug_assert!(top.add(2) <= self.current_call_info().top());
578 self.set_stack_top(top.add(2));
579 return Ok(iter + 1);
580 }
581 iter += 1;
582 }
583
584 Ok(-1)
585 }
586 }
587
588 pub unsafe fn clear_table(&self, index: i32) -> VmErrorResult {
590 unsafe {
591 let object = self.to_object(index).unwrap_unchecked();
592 debug_assert!(object.is_table());
593
594 let table = object.table_value();
595 if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
596 return self.readonly_error();
597 }
598
599 table.clear();
600 }
601 Ok(())
602 }
603
604 pub unsafe fn clone_table(&self, index: i32) -> VmErrorResult {
606 unsafe {
607 if flags::LuauCloneTableFix.get() {
608 self.check_gc()?;
609 self.thread_barrier();
610 }
611 self.ensure_stack(self, 1)?;
612
613 let object = self.to_object(index).unwrap_unchecked();
614 debug_assert!(object.is_table());
615
616 let cloned = self.clone_table_internal(object.table_value())?;
617 let top = self.stack_top();
618 top.value_unchecked().set_table_value(cloned);
619 debug_assert!(top < self.current_call_info().top());
620 self.set_stack_top(top.add(1));
621 }
622 Ok(())
623 }
624}
625
626impl Thread {
628 unsafe fn ref_in_registry(
629 &self,
630 index: i32,
631 registry: crate::Table,
632 registry_free: i32,
633 ) -> VmErrorResult<(i32, i32)> {
634 let Some(object) = (unsafe { self.to_object(index) }) else {
635 return Ok((LUA_REFNIL, registry_free));
636 };
637 if object.is_nil() {
638 return Ok((LUA_REFNIL, registry_free));
639 }
640
641 unsafe {
642 let reference = if registry_free != 0 {
643 registry_free
644 } else {
645 registry.getn() + 1
646 };
647
648 let slot = self.set_num(registry, reference)?;
649 let next_free = if registry_free != 0 {
650 slot.number_value() as i32
651 } else {
652 registry_free
653 };
654
655 slot.set_obj(object);
656 if object.is_collectable() {
657 self.barrier_table(registry, object.gc_value());
658 }
659
660 Ok((reference, next_free))
661 }
662 }
663
664 unsafe fn unref_in_registry(
665 &self,
666 reference: i32,
667 registry: crate::Table,
668 registry_free: i32,
669 ) -> i32 {
670 if reference <= LUA_REFNIL {
671 return registry_free;
672 }
673
674 unsafe {
675 let slot = registry.get_num(reference);
676 debug_assert!(slot != crate::value::nil_object());
677 slot.set_number(registry_free as f64);
678 }
679
680 reference
681 }
682
683 pub unsafe fn get_ref(&self, reference: i32) -> VmErrorResult<i32> {
685 unsafe { self.raw_geti(LUA_REGISTRY_INDEX, reference) }
686 }
687
688 pub unsafe fn ref_value(&self, index: i32) -> VmErrorResult<i32> {
690 debug_assert!(index != LUA_REGISTRY_INDEX);
691 unsafe {
692 let global = self.global();
693 let (reference, registry_free) = self.ref_in_registry(
694 index,
695 global.registry().table_value(),
696 global.registry_free(),
697 )?;
698 global.set_registry_free(registry_free);
699 Ok(reference)
700 }
701 }
702
703 pub unsafe fn unref_value(&self, reference: i32) -> i32 {
705 unsafe {
706 let global = self.global();
707 let registry_free = self.unref_in_registry(
708 reference,
709 global.registry().table_value(),
710 global.registry_free(),
711 );
712 global.set_registry_free(registry_free);
713 }
714
715 LUA_NOREF
716 }
717
718 pub unsafe fn weak_ref_value(&self, index: i32) -> VmErrorResult<i32> {
720 debug_assert!(flags::LuauGcTraceUdata.get());
721
722 unsafe {
723 let global = self.global();
724 let (reference, registry_free) = self.ref_in_registry(
725 index,
726 global.weak_registry().table_value(),
727 global.weak_registry_free(),
728 )?;
729 global.set_weak_registry_free(registry_free);
730 Ok(reference)
731 }
732 }
733
734 pub unsafe fn weak_unref_value(&self, reference: i32) -> i32 {
736 debug_assert!(flags::LuauGcTraceUdata.get());
737
738 unsafe {
739 let global = self.global();
740 let registry_free = self.unref_in_registry(
741 reference,
742 global.weak_registry().table_value(),
743 global.weak_registry_free(),
744 );
745 global.set_weak_registry_free(registry_free);
746 }
747
748 LUA_NOREF
749 }
750
751 pub unsafe fn get_weak_ref(&self, reference: i32) -> VmErrorResult<i32> {
753 debug_assert!(flags::LuauGcTraceUdata.get());
754
755 unsafe {
756 self.thread_barrier();
757 self.ensure_stack(self, 1)?;
758
759 let global = self.global();
760 let registry = global.weak_registry().table_value();
761 let top = self.stack_top();
762 top.value_unchecked().set_obj(registry.get_num(reference));
763 self.set_stack_top(top.add(1));
764 Ok(top.value_unchecked().tt())
765 }
766 }
767}
768
769impl Thread {
771 pub unsafe fn get_metafield(&self, index: i32, event: &str) -> VmErrorResult<i32> {
773 unsafe {
774 if self.get_metatable(index)? == 0 {
775 return Ok(0);
776 }
777
778 self.push_optional_string(Some(event))?;
779 let result = self.raw_get(-2);
780 if result == LUA_TNIL {
781 self.pop(2);
782 Ok(0)
783 } else {
784 self.remove(-2);
785 Ok(1)
786 }
787 }
788 }
789
790 pub unsafe fn call_meta(&self, object: i32, event: &str) -> VmResult<i32> {
792 unsafe {
793 let object = self.abs_index(object);
794 if self.get_metafield(object, event)? == 0 {
795 return Ok(0);
796 }
797
798 self.push_value(object)?;
799 self.call(1, 1)?;
800 Ok(1)
801 }
802 }
803
804 pub unsafe fn new_metatable(&self, table_name: &str) -> VmErrorResult<i32> {
806 let result = unsafe { self.raw_get_field(LUA_REGISTRY_INDEX, table_name)? };
807 if result != LUA_TNIL {
808 return Ok(0);
809 }
810
811 unsafe {
812 self.pop(1);
813 self.create_table(0, 0)?;
814 self.push_value(-1)?;
815
816 let registry = self.global().registry().table_value();
817 let key = self.intern_string(table_name.as_bytes().as_bstr())?;
818 let node_cursor = self.set_str(registry, key)?;
819 let value = self.stack_top().sub(1);
820 node_cursor
821 .node_unchecked()
822 .value_unchecked()
823 .set_obj(value.value_unchecked());
824 self.barrier_value(registry.into(), value.value_unchecked());
825 self.pop(1);
826 }
827
828 Ok(1)
829 }
830
831 pub unsafe fn find_table<'a, T>(
833 &self,
834 index: i32,
835 name: &'a T,
836 size_hint: usize,
837 ) -> VmErrorResult<Option<&'a [u8]>>
838 where
839 T: AsRef<[u8]> + ?Sized,
840 {
841 let mut remainder = name.as_ref();
842 unsafe {
843 self.push_value(index)?;
844
845 loop {
846 let separator = remainder.iter().position(|byte| *byte == b'.');
847 let (field, rest, has_more) = match separator {
848 Some(pos) => (&remainder[..pos], &remainder[pos + 1..], true),
849 None => (remainder, &[][..], false),
850 };
851
852 self.push_string(field)?;
853 let result = self.raw_get(-2);
854 if result == LUA_TNIL {
855 self.pop(1);
856 self.create_table(0, if has_more { 1 } else { size_hint })?;
857 self.push_string(field)?;
858 self.push_value(-2)?;
859 self.raw_set(-4)?;
860 } else if result != LUA_TTABLE {
861 self.pop(2);
862 return Ok(Some(remainder));
863 }
864
865 self.remove(-2);
866 if !has_more {
867 return Ok(None);
868 }
869
870 remainder = rest;
871 }
872 }
873 }
874
875 pub unsafe fn register(
877 &self,
878 library_name: Option<&str>,
879 functions: &[NativeFunction],
880 ) -> VmResult {
881 unsafe {
882 if let Some(library_name) = library_name {
883 if self.find_table(LUA_REGISTRY_INDEX, "_LOADED", 1)?.is_some() {
884 return crate::error!(self, "name conflict for module '_LOADED'")
885 .map_err(Into::into);
886 }
887
888 let result = self.get_field(-1, library_name)?;
889 if result != LUA_TTABLE {
890 self.pop(1);
891
892 if let Some(problem) =
893 self.find_table(LUA_GLOBALS_INDEX, library_name, functions.len())?
894 {
895 return crate::error!(self, "name conflict for module '%s'", &problem)
896 .map_err(Into::into);
897 }
898
899 self.push_value(-1)?;
900 self.set_field(-3, library_name)?;
901 }
902
903 self.remove(-2);
904 }
905
906 for entry in functions {
907 self.push_native_closure_k(entry.function, Some(entry.name), 0, None)?;
908 self.set_field(-2, entry.name)?;
909 }
910 }
911 Ok(())
912 }
913
914 pub unsafe fn register_module(&self, module: &NativeModule) -> VmResult {
915 unsafe { self.register(module.name, module.functions) }
916 }
917}
918
919impl Thread {
921 pub unsafe fn sandbox(&self) -> VmErrorResult {
928 unsafe {
929 self.push_nil()?;
930 while self.next(LUA_GLOBALS_INDEX)? != 0 {
931 if self.type_of(-1) == LUA_TTABLE {
932 self.set_readonly(-1, 1);
933 }
934
935 self.pop(1);
936 }
937
938 self.push_string("")?;
939 if self.get_metatable(-1)? != 0 {
940 self.set_readonly(-1, 1);
941 self.pop(2);
942 } else {
943 self.pop(1);
944 }
945
946 self.set_readonly(LUA_GLOBALS_INDEX, 1);
947 self.set_safe_env(LUA_GLOBALS_INDEX, 1);
948 }
949 Ok(())
950 }
951
952 pub unsafe fn sandbox_thread(&self) -> VmErrorResult {
961 unsafe {
962 self.create_table(0, 0)?;
963 self.create_table(0, 0)?;
964 self.push_value(LUA_GLOBALS_INDEX)?;
965
966 let metatable = self.to_object(-2).unwrap_unchecked().table_value();
967 let key = self.intern_string(b"__index".as_bstr())?;
968 let node_cursor = self.set_str(metatable, key)?;
969 let value = self.stack_top().sub(1);
970
971 node_cursor
972 .node_unchecked()
973 .value_unchecked()
974 .set_obj(value.value_unchecked());
975 self.barrier_value(metatable.into(), value.value_unchecked());
976 self.pop(1);
977 self.set_readonly(-1, 1);
978 self.set_metatable(-2)?;
979 self.replace(LUA_GLOBALS_INDEX);
980 self.set_safe_env(LUA_GLOBALS_INDEX, 1);
981 }
982 Ok(())
983 }
984}