luau-vm 0.732.0

Pure-Rust Luau virtual machine, garbage collector, and standard libraries
Documentation
use luau_common::BString;

use crate::VmErrorResult;
use crate::gc::{GCS_PAUSE, GcCategoryNamer, GcRuntime};
use crate::gc::{
    LUA_GC_COLLECT, LUA_GC_COUNT, LUA_GC_COUNT_B, LUA_GC_IS_PAUSED, LUA_GC_IS_RUNNING,
    LUA_GC_RESTART, LUA_GC_SET_GOAL, LUA_GC_SET_STEP_MUL, LUA_GC_SET_STEP_SIZE, LUA_GC_STEP,
    LUA_GC_STOP,
};
use crate::handle::RawHandle;
use crate::thread::Thread;

impl Thread {
    /// `lua_gc`
    pub unsafe fn gc(&self, what: i32, data: i32) -> VmErrorResult<i32> {
        let global = unsafe { self.global() };
        let mut result = 0;

        unsafe {
            match what {
                LUA_GC_STOP => {
                    global.as_ptr().as_mut().unwrap_unchecked().gc_threshold = usize::MAX;
                }
                LUA_GC_RESTART => {
                    let global_mut = global.as_ptr().as_mut().unwrap_unchecked();
                    global_mut.gc_threshold = global_mut.total_bytes;
                }
                LUA_GC_COLLECT => {
                    self.full_gc();
                }
                LUA_GC_COUNT => {
                    result = (global.as_ptr().as_ref().unwrap_unchecked().total_bytes >> 10) as i32;
                }
                LUA_GC_COUNT_B => {
                    result =
                        (global.as_ptr().as_ref().unwrap_unchecked().total_bytes & 1023) as i32;
                }
                LUA_GC_IS_RUNNING => {
                    result = i32::from(
                        global.as_ptr().as_ref().unwrap_unchecked().gc_threshold != usize::MAX,
                    );
                }
                LUA_GC_STEP => {
                    let amount = (data as usize) << 10;
                    let old_credit = if global.gc_state() == GCS_PAUSE {
                        0
                    } else {
                        global.as_ptr().as_ref().unwrap_unchecked().gc_threshold as isize
                            - global.as_ptr().as_ref().unwrap_unchecked().total_bytes as isize
                    };

                    if amount <= global.as_ptr().as_ref().unwrap_unchecked().total_bytes {
                        let global_mut = global.as_ptr().as_mut().unwrap_unchecked();
                        global_mut.gc_threshold = global_mut.total_bytes - amount;
                    } else {
                        global.as_ptr().as_mut().unwrap_unchecked().gc_threshold = 0;
                    }

                    let mut actual_work = 0usize;
                    while global.as_ptr().as_ref().unwrap_unchecked().gc_threshold
                        <= global.as_ptr().as_ref().unwrap_unchecked().total_bytes
                    {
                        actual_work += self.step(false)?;

                        if global.gc_state() == GCS_PAUSE {
                            result = 1;
                            break;
                        }
                    }

                    if global.gc_state() != GCS_PAUSE {
                        let total_bytes = global.as_ptr().as_ref().unwrap_unchecked().total_bytes;
                        let new_threshold =
                            total_bytes as isize + actual_work as isize + old_credit;
                        global.as_ptr().as_mut().unwrap_unchecked().gc_threshold =
                            if new_threshold < 0 {
                                0
                            } else {
                                new_threshold as usize
                            };
                    }
                }
                LUA_GC_SET_GOAL => {
                    result = global.as_ptr().as_ref().unwrap_unchecked().gc_goal;
                    global.as_ptr().as_mut().unwrap_unchecked().gc_goal = data;
                }
                LUA_GC_SET_STEP_MUL => {
                    result = global.as_ptr().as_ref().unwrap_unchecked().gc_step_mul;
                    global.as_ptr().as_mut().unwrap_unchecked().gc_step_mul = data;
                }
                LUA_GC_SET_STEP_SIZE => {
                    result = global.as_ptr().as_ref().unwrap_unchecked().gc_step_size >> 10;
                    global.as_ptr().as_mut().unwrap_unchecked().gc_step_size = data << 10;
                }
                LUA_GC_IS_PAUSED => {
                    result = i32::from(global.gc_state() == GCS_PAUSE);
                }
                _ => result = -1,
            }
        }

        Ok(result)
    }

    /// `lua_memorydump`
    pub unsafe fn memory_dump(
        &self,
        output: &mut BString,
        category_name: Option<&mut dyn GcCategoryNamer>,
    ) {
        unsafe { <Self as GcRuntime>::dump(self, (output as *mut BString).cast(), category_name) };
    }

    /// `lua_allocationrate`
    pub unsafe fn allocation_rate(&self) -> i64 {
        unsafe { <Self as GcRuntime>::allocation_rate(self) }
    }

    /// `lua_setmemcat`
    pub unsafe fn set_mem_cat(&self, category: i32) {
        debug_assert!((category as u32) < crate::memory::LUA_MEMORY_CATEGORIES as u32);
        unsafe {
            self.as_ptr().as_mut().unwrap_unchecked().active_memcat = category as u8;
        }
    }

    /// `lua_totalbytes`
    pub unsafe fn total_bytes(&self, category: i32) -> usize {
        debug_assert!(category < crate::memory::LUA_MEMORY_CATEGORIES as i32);

        let global_handle = unsafe { self.global() };
        let global = unsafe { global_handle.as_ptr().as_ref().unwrap_unchecked() };
        if category < 0 {
            global.total_bytes
        } else {
            global.memcat_bytes[category as usize]
        }
    }
}