use core::mem::MaybeUninit;
use core::ptr::{self, NonNull};
use luau_common::{ByteSlice, flags};
use super::stack::RawStackAccess;
use super::{LUA_GLOBALS_INDEX, LUA_NOREF, LUA_REFNIL, LUA_REGISTRY_INDEX, Thread};
use crate::debug::DebugRuntime;
use crate::gc::{GcBarrier, GcRuntime};
use crate::handle::RawHandle;
use crate::native::{NativeFunction, NativeModule};
use crate::state::ThreadState;
use crate::string::StringRuntime;
use crate::table::TableRuntime;
use crate::types::{LUA_TFUNCTION, LUA_TNIL, LUA_TOBJECT, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA};
use crate::value::{RAW_TVALUE_NIL, TValue};
use crate::vm::VmOperations;
use crate::{VmErrorResult, VmResult};
impl Thread {
pub unsafe fn create_table(&self, array_size: usize, record_size: usize) -> VmErrorResult {
let (Ok(array_size), Ok(record_size)) =
(i32::try_from(array_size), i32::try_from(record_size))
else {
return unsafe { crate::run_error!(self, "table overflow") };
};
unsafe {
self.check_gc()?;
self.thread_barrier();
self.ensure_stack(self, 1)?;
let table = self.new_table_internal(array_size, record_size)?;
let top = self.stack_top();
top.value_unchecked().set_table_value(table);
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn new_table(&self) -> VmErrorResult {
unsafe { self.create_table(0, 0) }
}
pub unsafe fn get_table(&self, index: i32) -> VmResult<i32> {
unsafe {
let top = self.stack_top().sub(1);
debug_assert!(top >= self.stack_base());
self.thread_barrier();
let table = self.to_object(index).unwrap_unchecked();
self.get_table_internal(table, top.value_unchecked(), top)?;
Ok(self.stack_top().sub(1).value_unchecked().tt())
}
}
pub unsafe fn get_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmResult<i32> {
let key = key.as_ref();
let mut key_storage = RAW_TVALUE_NIL;
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let table = self.to_object(index).unwrap_unchecked();
let mut table_storage = MaybeUninit::uninit();
ptr::copy_nonoverlapping(table.as_ptr(), table_storage.as_mut_ptr(), 1);
let table = TValue::from_raw(NonNull::new_unchecked(table_storage.as_mut_ptr()));
let key_value = TValue::from_mut(&mut key_storage);
key_value.set_string_value(self.intern_string(key.as_bstr())?);
let top = self.stack_top();
self.get_table_internal(table, key_value, top)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
Ok(top.value_unchecked().tt())
}
}
pub unsafe fn get_global(&self, name: impl AsRef<[u8]>) -> VmResult<i32> {
unsafe { self.get_field(LUA_GLOBALS_INDEX, name) }
}
pub unsafe fn raw_get_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmErrorResult<i32> {
let key = key.as_ref();
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
let string = self.intern_string(key.as_bstr())?;
let top = self.stack_top();
top.value_unchecked().set_obj(table.get_str(string));
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
Ok(top.value_unchecked().tt())
}
}
pub unsafe fn raw_get(&self, index: i32) -> i32 {
unsafe {
self.thread_barrier();
let table = self.to_object(index).unwrap_unchecked();
debug_assert!(table.is_table());
let top = self.stack_top().sub(1);
top.value_unchecked()
.set_obj(table.table_value().get(top.value_unchecked()));
top.value_unchecked().tt()
}
}
pub unsafe fn raw_geti(&self, index: i32, key: i32) -> VmErrorResult<i32> {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let table = self.to_object(index).unwrap_unchecked();
debug_assert!(table.is_table());
let top = self.stack_top();
top.value_unchecked()
.set_obj(table.table_value().get_num(key));
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
Ok(top.value_unchecked().tt())
}
}
pub unsafe fn raw_getp_tagged(&self, index: i32, key: *mut (), tag: i32) -> VmErrorResult<i32> {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let table = self.to_object(index).unwrap_unchecked();
debug_assert!(table.is_table());
let top = self.stack_top();
top.value_unchecked()
.set_obj(table.table_value().getp(key, tag));
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
Ok(top.value_unchecked().tt())
}
}
pub unsafe fn raw_getp(&self, index: i32, key: *mut ()) -> VmErrorResult<i32> {
unsafe { self.raw_getp_tagged(index, key, 0) }
}
pub unsafe fn set_readonly(&self, index: i32, enabled: i32) {
unsafe {
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
let registry = self.global().registry().table_value();
debug_assert!(table != registry);
table.as_ptr().as_mut().unwrap_unchecked().readonly = u8::from(enabled != 0);
}
}
pub unsafe fn get_readonly(&self, index: i32) -> i32 {
unsafe {
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
i32::from(
object
.table_value()
.as_ptr()
.as_ref()
.unwrap_unchecked()
.readonly
!= 0,
)
}
}
pub unsafe fn set_safe_env(&self, index: i32, enabled: i32) {
let object = unsafe { self.to_object(index).unwrap_unchecked() };
debug_assert!(object.is_table());
unsafe {
object
.table_value()
.as_ptr()
.as_mut()
.unwrap_unchecked()
.safe_env = u8::from(enabled != 0);
}
}
pub unsafe fn get_metatable(&self, index: i32) -> VmErrorResult<i32> {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let object = self.to_object(index);
let metatable = if let Some(object) = object {
match object.tt() {
x if x == LUA_TTABLE => object.table_value().metatable(),
x if x == LUA_TUSERDATA => object.userdata_value().metatable(),
x if x == LUA_TOBJECT => object.object_value().class().instance_metatable(),
tag => self.global().metatable(tag as usize),
}
} else {
None
};
if let Some(metatable) = metatable {
let top = self.stack_top();
top.value_unchecked().set_table_value(metatable);
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
Ok(1)
} else {
Ok(0)
}
}
}
pub unsafe fn get_fenv(&self, index: i32) -> VmErrorResult {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let object = self.to_object(index).unwrap_unchecked();
let top = self.stack_top();
match object.tt() {
x if x == LUA_TFUNCTION => top
.value_unchecked()
.set_table_value(object.closure_value().env()),
x if x == LUA_TTHREAD => top
.value_unchecked()
.set_table_value(object.thread_value().globals()),
_ => top.value_unchecked().set_nil(),
}
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn set_table(&self, index: i32) -> VmResult {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) >= 2);
let table = self.to_object(index).unwrap_unchecked();
let key = top.sub(2).value_unchecked();
let value = top.sub(1).value_unchecked();
self.set_table_internal(table, key, value)?;
self.set_stack_top(self.stack_top().sub(2));
}
Ok(())
}
pub unsafe fn set_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmResult {
let key = key.as_ref();
let mut key_storage = RAW_TVALUE_NIL;
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
let table = self.to_object(index).unwrap_unchecked();
let mut table_storage = MaybeUninit::uninit();
ptr::copy_nonoverlapping(table.as_ptr(), table_storage.as_mut_ptr(), 1);
let table = TValue::from_raw(NonNull::new_unchecked(table_storage.as_mut_ptr()));
let key_value = TValue::from_mut(&mut key_storage);
key_value.set_string_value(self.intern_string(key.as_bstr())?);
let value = top.sub(1).value_unchecked();
self.set_table_internal(table, key_value, value)?;
self.set_stack_top(self.stack_top().sub(1));
}
Ok(())
}
pub unsafe fn raw_set_field(&self, index: i32, key: impl AsRef<[u8]>) -> VmErrorResult {
let key = key.as_ref();
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
let value = top.sub(1).value_unchecked();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return self.readonly_error();
}
let string = self.intern_string(key.as_bstr())?;
let node_cursor = self.set_str(table, string)?;
node_cursor
.node_unchecked()
.value_unchecked()
.set_obj(value);
if value.is_collectable() {
self.barrier_table(table, value.gc_value());
}
self.set_stack_top(top.sub(1));
}
Ok(())
}
pub unsafe fn set_global(&self, name: impl AsRef<[u8]>) -> VmErrorResult {
unsafe { self.raw_set_field(LUA_GLOBALS_INDEX, name) }
}
pub unsafe fn concat(&self, count: i32) -> VmResult {
debug_assert!(count >= 0);
if count >= 2 {
unsafe {
self.check_gc()?;
self.thread_barrier();
self.concat_internal(count, self.get_top() - 1)?;
self.set_stack_top(self.stack_top().sub((count - 1) as usize));
}
} else if count == 0 {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
self.push_string("")?;
}
}
Ok(())
}
pub unsafe fn raw_set(&self, index: i32) -> VmErrorResult {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) >= 2);
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
let key = top.sub(2).value_unchecked();
let value = top.sub(1).value_unchecked();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return self.readonly_error();
}
let slot = self.set(table, key)?;
slot.set_obj(value);
if value.is_collectable() {
self.barrier_table(table, value.gc_value());
}
self.set_stack_top(top.sub(2));
}
Ok(())
}
pub unsafe fn raw_seti(&self, index: i32, key: i32) -> VmErrorResult {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
let value = top.sub(1).value_unchecked();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return self.readonly_error();
}
let slot = self.set_num(table, key)?;
slot.set_obj(value);
if value.is_collectable() {
self.barrier_table(table, value.gc_value());
}
self.set_stack_top(top.sub(1));
}
Ok(())
}
pub unsafe fn raw_setp_tagged(&self, index: i32, key: *mut (), tag: i32) -> VmErrorResult {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
let value = top.sub(1).value_unchecked();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return self.readonly_error();
}
let slot = self.setp(table, key, tag)?;
slot.set_obj(value);
if value.is_collectable() {
self.barrier_table(table, value.gc_value());
}
self.set_stack_top(top.sub(1));
}
Ok(())
}
pub unsafe fn raw_setp(&self, index: i32, key: *mut ()) -> VmErrorResult {
unsafe { self.raw_setp_tagged(index, key, 0) }
}
pub unsafe fn set_metatable(&self, index: i32) -> VmErrorResult<i32> {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
let object = self.to_object(index).unwrap_unchecked();
let metatable_value = top.sub(1).value_unchecked();
let metatable = if metatable_value.is_nil() {
None
} else {
debug_assert!(metatable_value.is_table());
Some(metatable_value.table_value())
};
match object.tt() {
x if x == LUA_TTABLE => {
let table = object.table_value();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return self.readonly_error();
}
table.set_metatable(metatable);
if let Some(metatable) = metatable {
self.object_barrier(table.into(), metatable.into());
}
}
x if x == LUA_TUSERDATA => {
let userdata = object.userdata_value();
userdata.set_metatable(metatable);
if let Some(metatable) = metatable {
self.object_barrier(userdata.into(), metatable.into());
}
}
tag => {
self.global().as_ptr().as_mut().unwrap_unchecked().mt[tag as usize] =
metatable.map_or(ptr::null_mut(), |table| table.as_ptr());
}
}
self.set_stack_top(top.sub(1));
Ok(1)
}
}
pub unsafe fn set_fenv(&self, index: i32) -> i32 {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
let object = self.to_object(index).unwrap_unchecked();
let environment = top.sub(1).value_unchecked();
debug_assert!(environment.is_table());
let environment = environment.table_value();
let result = match object.tt() {
x if x == LUA_TFUNCTION => {
let closure = object.closure_value();
closure.set_env(environment);
self.object_barrier(closure.into(), environment.into());
1
}
x if x == LUA_TTHREAD => {
let thread = object.thread_value();
thread.set_globals(environment);
self.object_barrier((&thread).into(), environment.into());
1
}
_ => 0,
};
self.set_stack_top(top.sub(1));
result
}
}
pub unsafe fn next(&self, index: i32) -> VmErrorResult<i32> {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
self.thread_barrier();
self.ensure_stack(self, 1)?;
let table = self.to_object(index).unwrap_unchecked();
debug_assert!(table.is_table());
let key = top.sub(1);
let more = self.next_internal(table.table_value(), key)?;
if more != 0 {
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
} else {
self.set_stack_top(top.sub(1));
}
Ok(more)
}
}
pub unsafe fn raw_iter(&self, index: i32, mut iter: i32) -> VmErrorResult<i32> {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 2)?;
let table = self.to_object(index).unwrap_unchecked();
debug_assert!(table.is_table());
debug_assert!(iter >= 0);
let table = table.table_value();
let size_array = table.as_ptr().as_ref().unwrap_unchecked().size_array;
while (iter as u32) < size_array as u32 {
let entry = table.array_slot(iter as usize);
if !entry.is_nil() {
let top = self.stack_top();
top.value_unchecked().set_number((iter + 1) as f64);
top.add(1).value_unchecked().set_obj(entry);
debug_assert!(top.add(2) <= self.current_call_info().top());
self.set_stack_top(top.add(2));
return Ok(iter + 1);
}
iter += 1;
}
let size_node = table.node_count();
while ((iter - size_array) as u32) < size_node as u32 {
let node = table.node(iter - size_array);
if !node.value_unchecked().is_nil() {
let top = self.stack_top();
node.write_key_to_value(top.value_unchecked());
top.add(1).value_unchecked().set_obj(node.value_unchecked());
debug_assert!(top.add(2) <= self.current_call_info().top());
self.set_stack_top(top.add(2));
return Ok(iter + 1);
}
iter += 1;
}
Ok(-1)
}
}
pub unsafe fn clear_table(&self, index: i32) -> VmErrorResult {
unsafe {
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let table = object.table_value();
if table.as_ptr().as_ref().unwrap_unchecked().readonly != 0 {
return self.readonly_error();
}
table.clear();
}
Ok(())
}
pub unsafe fn clone_table(&self, index: i32) -> VmErrorResult {
unsafe {
if flags::LuauCloneTableFix.get() {
self.check_gc()?;
self.thread_barrier();
}
self.ensure_stack(self, 1)?;
let object = self.to_object(index).unwrap_unchecked();
debug_assert!(object.is_table());
let cloned = self.clone_table_internal(object.table_value())?;
let top = self.stack_top();
top.value_unchecked().set_table_value(cloned);
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
}
Ok(())
}
}
impl Thread {
unsafe fn ref_in_registry(
&self,
index: i32,
registry: crate::Table,
registry_free: i32,
) -> VmErrorResult<(i32, i32)> {
let Some(object) = (unsafe { self.to_object(index) }) else {
return Ok((LUA_REFNIL, registry_free));
};
if object.is_nil() {
return Ok((LUA_REFNIL, registry_free));
}
unsafe {
let reference = if registry_free != 0 {
registry_free
} else {
registry.getn() + 1
};
let slot = self.set_num(registry, reference)?;
let next_free = if registry_free != 0 {
slot.number_value() as i32
} else {
registry_free
};
slot.set_obj(object);
if object.is_collectable() {
self.barrier_table(registry, object.gc_value());
}
Ok((reference, next_free))
}
}
unsafe fn unref_in_registry(
&self,
reference: i32,
registry: crate::Table,
registry_free: i32,
) -> i32 {
if reference <= LUA_REFNIL {
return registry_free;
}
unsafe {
let slot = registry.get_num(reference);
debug_assert!(slot != crate::value::nil_object());
slot.set_number(registry_free as f64);
}
reference
}
pub unsafe fn get_ref(&self, reference: i32) -> VmErrorResult<i32> {
unsafe { self.raw_geti(LUA_REGISTRY_INDEX, reference) }
}
pub unsafe fn ref_value(&self, index: i32) -> VmErrorResult<i32> {
debug_assert!(index != LUA_REGISTRY_INDEX);
unsafe {
let global = self.global();
let (reference, registry_free) = self.ref_in_registry(
index,
global.registry().table_value(),
global.registry_free(),
)?;
global.set_registry_free(registry_free);
Ok(reference)
}
}
pub unsafe fn unref_value(&self, reference: i32) -> i32 {
unsafe {
let global = self.global();
let registry_free = self.unref_in_registry(
reference,
global.registry().table_value(),
global.registry_free(),
);
global.set_registry_free(registry_free);
}
LUA_NOREF
}
pub unsafe fn weak_ref_value(&self, index: i32) -> VmErrorResult<i32> {
debug_assert!(flags::LuauGcTraceUdata.get());
unsafe {
let global = self.global();
let (reference, registry_free) = self.ref_in_registry(
index,
global.weak_registry().table_value(),
global.weak_registry_free(),
)?;
global.set_weak_registry_free(registry_free);
Ok(reference)
}
}
pub unsafe fn weak_unref_value(&self, reference: i32) -> i32 {
debug_assert!(flags::LuauGcTraceUdata.get());
unsafe {
let global = self.global();
let registry_free = self.unref_in_registry(
reference,
global.weak_registry().table_value(),
global.weak_registry_free(),
);
global.set_weak_registry_free(registry_free);
}
LUA_NOREF
}
pub unsafe fn get_weak_ref(&self, reference: i32) -> VmErrorResult<i32> {
debug_assert!(flags::LuauGcTraceUdata.get());
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let global = self.global();
let registry = global.weak_registry().table_value();
let top = self.stack_top();
top.value_unchecked().set_obj(registry.get_num(reference));
self.set_stack_top(top.add(1));
Ok(top.value_unchecked().tt())
}
}
}
impl Thread {
pub unsafe fn get_metafield(&self, index: i32, event: &str) -> VmErrorResult<i32> {
unsafe {
if self.get_metatable(index)? == 0 {
return Ok(0);
}
self.push_optional_string(Some(event))?;
let result = self.raw_get(-2);
if result == LUA_TNIL {
self.pop(2);
Ok(0)
} else {
self.remove(-2);
Ok(1)
}
}
}
pub unsafe fn call_meta(&self, object: i32, event: &str) -> VmResult<i32> {
unsafe {
let object = self.abs_index(object);
if self.get_metafield(object, event)? == 0 {
return Ok(0);
}
self.push_value(object)?;
self.call(1, 1)?;
Ok(1)
}
}
pub unsafe fn new_metatable(&self, table_name: &str) -> VmErrorResult<i32> {
let result = unsafe { self.raw_get_field(LUA_REGISTRY_INDEX, table_name)? };
if result != LUA_TNIL {
return Ok(0);
}
unsafe {
self.pop(1);
self.create_table(0, 0)?;
self.push_value(-1)?;
let registry = self.global().registry().table_value();
let key = self.intern_string(table_name.as_bytes().as_bstr())?;
let node_cursor = self.set_str(registry, key)?;
let value = self.stack_top().sub(1);
node_cursor
.node_unchecked()
.value_unchecked()
.set_obj(value.value_unchecked());
self.barrier_value(registry.into(), value.value_unchecked());
self.pop(1);
}
Ok(1)
}
pub unsafe fn find_table<'a, T>(
&self,
index: i32,
name: &'a T,
size_hint: usize,
) -> VmErrorResult<Option<&'a [u8]>>
where
T: AsRef<[u8]> + ?Sized,
{
let mut remainder = name.as_ref();
unsafe {
self.push_value(index)?;
loop {
let separator = remainder.iter().position(|byte| *byte == b'.');
let (field, rest, has_more) = match separator {
Some(pos) => (&remainder[..pos], &remainder[pos + 1..], true),
None => (remainder, &[][..], false),
};
self.push_string(field)?;
let result = self.raw_get(-2);
if result == LUA_TNIL {
self.pop(1);
self.create_table(0, if has_more { 1 } else { size_hint })?;
self.push_string(field)?;
self.push_value(-2)?;
self.raw_set(-4)?;
} else if result != LUA_TTABLE {
self.pop(2);
return Ok(Some(remainder));
}
self.remove(-2);
if !has_more {
return Ok(None);
}
remainder = rest;
}
}
}
pub unsafe fn register(
&self,
library_name: Option<&str>,
functions: &[NativeFunction],
) -> VmResult {
unsafe {
if let Some(library_name) = library_name {
if self.find_table(LUA_REGISTRY_INDEX, "_LOADED", 1)?.is_some() {
return crate::error!(self, "name conflict for module '_LOADED'")
.map_err(Into::into);
}
let result = self.get_field(-1, library_name)?;
if result != LUA_TTABLE {
self.pop(1);
if let Some(problem) =
self.find_table(LUA_GLOBALS_INDEX, library_name, functions.len())?
{
return crate::error!(self, "name conflict for module '%s'", &problem)
.map_err(Into::into);
}
self.push_value(-1)?;
self.set_field(-3, library_name)?;
}
self.remove(-2);
}
for entry in functions {
self.push_native_closure_k(entry.function, Some(entry.name), 0, None)?;
self.set_field(-2, entry.name)?;
}
}
Ok(())
}
pub unsafe fn register_module(&self, module: &NativeModule) -> VmResult {
unsafe { self.register(module.name, module.functions) }
}
}
impl Thread {
pub unsafe fn sandbox(&self) -> VmErrorResult {
unsafe {
self.push_nil()?;
while self.next(LUA_GLOBALS_INDEX)? != 0 {
if self.type_of(-1) == LUA_TTABLE {
self.set_readonly(-1, 1);
}
self.pop(1);
}
self.push_string("")?;
if self.get_metatable(-1)? != 0 {
self.set_readonly(-1, 1);
self.pop(2);
} else {
self.pop(1);
}
self.set_readonly(LUA_GLOBALS_INDEX, 1);
self.set_safe_env(LUA_GLOBALS_INDEX, 1);
}
Ok(())
}
pub unsafe fn sandbox_thread(&self) -> VmErrorResult {
unsafe {
self.create_table(0, 0)?;
self.create_table(0, 0)?;
self.push_value(LUA_GLOBALS_INDEX)?;
let metatable = self.to_object(-2).unwrap_unchecked().table_value();
let key = self.intern_string(b"__index".as_bstr())?;
let node_cursor = self.set_str(metatable, key)?;
let value = self.stack_top().sub(1);
node_cursor
.node_unchecked()
.value_unchecked()
.set_obj(value.value_unchecked());
self.barrier_value(metatable.into(), value.value_unchecked());
self.pop(1);
self.set_readonly(-1, 1);
self.set_metatable(-2)?;
self.replace(LUA_GLOBALS_INDEX);
self.set_safe_env(LUA_GLOBALS_INDEX, 1);
}
Ok(())
}
}