luau-vm 0.732.0

Pure-Rust Luau virtual machine, garbage collector, and standard libraries
Documentation
use crate::buffer::BufferRuntime;
use crate::call::ProtectedCall;
use crate::class::ClassRuntime;
use crate::function::FunctionRuntime;
use crate::gc::GcObject;
use crate::handle::RawHandle;
use crate::memory::LuaPage;
use crate::state::ThreadLifecycle;
use crate::state::ThreadState;
use crate::string::LUA_MIN_STRING_TABLE_SIZE;
use crate::string::StringRuntime;
use crate::table::TableRuntime;
use crate::thread::Thread;
use crate::types;
use crate::userdata::UserdataRuntime;
use crate::{VmError, VmExit, VmResult};

impl Thread {
    /// `freeobj`
    unsafe fn free_obj(&self, object: GcObject, page: LuaPage) {
        unsafe {
            match object.as_ptr().as_ref().unwrap_unchecked().tt as i32 {
                x if x == types::LUA_TPROTO => self.free_proto(object.to_proto(), page),
                x if x == types::LUA_TFUNCTION => self.free_closure(object.to_closure(), page),
                x if x == types::LUA_TUPVALUE => self.free_upvalue(object.to_upvalue(), page),
                x if x == types::LUA_TTABLE => self.free_table(object.to_table(), page),
                x if x == types::LUA_TTHREAD => {
                    let state = object.to_state();
                    let global = self.global();
                    debug_assert!(state != *self);
                    debug_assert!(state != global.main_thread());
                    self.free_thread(&state, page);
                }
                x if x == types::LUA_TSTRING => self.free_string(object.to_string(), page),
                x if x == types::LUA_TUSERDATA => self.free_userdata(object.to_userdata(), page),
                x if x == types::LUA_TBUFFER => self.free_buffer(object.to_buffer(), page),
                x if x == types::LUA_TCLASS => self.free_class(object.to_class(), page),
                x if x == types::LUA_TOBJECT => self.free_object(object.to_object(), page),
                other => unreachable!("unsupported sweep object {}", other),
            }
        }
    }

    /// `stringresizeprotected`
    unsafe fn resize_string_protected(&self, new_size: i32) {
        #[repr(C)]
        struct ResizeStringContext {
            new_size: i32,
        }

        unsafe fn resize_string_trampoline(
            thread: &Thread,
            context: &mut ResizeStringContext,
        ) -> VmResult {
            unsafe { thread.resize(context.new_size) }?;
            Ok(())
        }

        let mut context = ResizeStringContext { new_size };
        let result = unsafe { self.raw_run_protected(resize_string_trampoline, &mut context) };
        debug_assert!(matches!(
            result,
            Ok(()) | Err(VmExit::Error(VmError::Memory))
        ));
    }

    /// `shrinkbuffers`
    pub(super) unsafe fn shrink_buffers(&self) {
        unsafe {
            let global = self.global();
            let string_table = &global.as_ptr().as_ref().unwrap_unchecked().string_table;
            if string_table.n_use < (string_table.size / 4) as u32
                && string_table.size > (LUA_MIN_STRING_TABLE_SIZE as i32) * 2
            {
                self.resize_string_protected(string_table.size / 2);
            }
        }
    }

    /// `shrinkbuffersfull`
    pub(super) unsafe fn shrink_buffers_full(&self) {
        unsafe {
            let global = self.global();
            let string_table = &global.as_ptr().as_ref().unwrap_unchecked().string_table;
            let mut hash_size = string_table.size;

            while string_table.n_use < (hash_size / 4) as u32
                && hash_size > (LUA_MIN_STRING_TABLE_SIZE as i32) * 2
            {
                hash_size /= 2;
            }

            if hash_size != string_table.size {
                self.resize_string_protected(hash_size);
            }
        }
    }
}

/// `deletegco`
pub(super) unsafe fn delete_gco(context: *mut (), page: LuaPage, object: GcObject) -> bool {
    unsafe {
        let thread = Thread::from_raw(NonNull::new_unchecked(context.cast()));
        thread.free_obj(object, page);
    }
    true
}

impl LuaPage {
    /// `sweepgcopage`
    pub(super) unsafe fn sweep_gco(&self, thread: &Thread) -> i32 {
        unsafe {
            let (mut walk, mut busy_blocks) = self.gco_walk();

            debug_assert!(busy_blocks > 0);

            let global = thread.global();
            let dead_mask =
                global.as_ptr().as_ref().unwrap_unchecked().current_white ^ crate::gc::WHITE_BITS;
            let new_white = global.white();
            let mask_marks = !(crate::gc::bit_mask(crate::gc::BLACK_BIT) | crate::gc::WHITE_BITS);

            while let Some(object) = walk.next() {
                let marked = object.as_ptr().as_ref().unwrap_unchecked().marked;
                if ((marked ^ crate::gc::WHITE_BITS) & dead_mask) != 0 {
                    debug_assert!(!global.is_dead(object));
                    object.as_ptr().as_mut().unwrap_unchecked().marked =
                        (marked & mask_marks) | new_white;
                } else {
                    debug_assert!(global.is_dead(object));
                    thread.free_obj(object, *self);
                    busy_blocks -= 1;
                    if busy_blocks == 0 {
                        return walk.steps();
                    }
                }
            }

            walk.steps()
        }
    }
}
use core::ptr::NonNull;