use core::ptr;
use luau_common::{BStr, ByteSlice, flags};
use super::{
LUA_ENVIRON_INDEX, LUA_GLOBALS_INDEX, LUA_REGISTRY_INDEX, LUA_TNONE, LUAI_MAX_C_STACK, Thread,
is_pseudo,
};
use crate::buffer::BufferRuntime;
use crate::call::{ProtectedCall, ThreadStack};
use crate::debug::DebugRuntime;
use crate::gc::{GcBarrier, GcRuntime};
use crate::handle::RawHandle;
use crate::handle::sealed::Sealed;
use crate::state::ThreadState;
use crate::string::{LuaString, StringFormatting, StringRuntime, TString};
use crate::types::{
LUA_T_COUNT, LUA_TBOOLEAN, LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TLIGHTUSERDATA,
LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
LUA_TVECTOR,
};
use crate::value::{RAW_TVALUE_NIL, TValue, TValueCursor, nil_object};
use crate::vm::{VmConversions, VmOperations};
use crate::{Class, Table, VmErrorResult, VmResult};
#[allow(
clippy::missing_safety_doc,
reason = "all methods share the capability-level safety contract"
)]
pub trait RawStackAccess: Sealed {
unsafe fn to_object(&self, index: i32) -> Option<TValue>;
unsafe fn push_value_internal(&self, value: TValue) -> VmErrorResult;
unsafe fn push_table(&self, table: Table) -> VmErrorResult;
unsafe fn push_class(&self, class: Class) -> VmErrorResult;
}
impl RawStackAccess for Thread {
unsafe fn to_object(&self, index: i32) -> Option<TValue> {
let object = unsafe { self.index_to_addr(index) };
if object == nil_object() {
None
} else {
Some(object)
}
}
unsafe fn push_value_internal(&self, value: TValue) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
top.value_unchecked().set_obj(value);
debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
self.set_stack_top(top.add(1));
}
Ok(())
}
unsafe fn push_table(&self, table: Table) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
top.value_unchecked().set_table_value(table);
debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
self.set_stack_top(top.add(1));
}
Ok(())
}
unsafe fn push_class(&self, class: Class) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
top.value_unchecked().set_class_value(class);
debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
self.set_stack_top(top.add(1));
}
Ok(())
}
}
impl Thread {
unsafe fn pseudo_to_addr(&self, index: i32) -> TValue {
debug_assert!(is_pseudo(index));
unsafe {
let global = self.global();
match index {
LUA_REGISTRY_INDEX => global.registry(),
LUA_ENVIRON_INDEX => {
let pseudo_temp = global.pseudo_temp();
pseudo_temp.set_table_value(self.current_env());
pseudo_temp
}
LUA_GLOBALS_INDEX => {
let pseudo_temp = global.pseudo_temp();
pseudo_temp.set_table_value(self.globals());
pseudo_temp
}
_ => {
let closure = self.current_function();
let upvalue_index = (LUA_GLOBALS_INDEX - index) as usize;
if upvalue_index
<= closure.as_ptr().as_ref().unwrap_unchecked().n_upvalues as usize
{
closure.native_upvalue(upvalue_index - 1)
} else {
nil_object()
}
}
}
}
}
pub(crate) unsafe fn stack_index_cursor(&self, index: i32) -> Option<TValueCursor> {
debug_assert!(index != 0);
unsafe {
if index > 0 {
let object = self.stack_base().add((index - 1) as usize);
debug_assert!(
index
<= self
.current_call_info()
.top()
.offset_from(self.stack_base()) as i32
);
Some(object)
} else if index > LUA_REGISTRY_INDEX {
debug_assert!(-index <= self.stack_top().offset_from(self.stack_base()) as i32);
Some(self.stack_top().offset(index as isize))
} else {
None
}
}
}
pub(crate) unsafe fn stack_index_to_cursor(&self, index: i32) -> TValueCursor {
debug_assert!(!is_pseudo(index));
unsafe { self.stack_index_cursor(index).unwrap_unchecked() }
}
pub(crate) unsafe fn index_to_addr(&self, index: i32) -> TValue {
unsafe {
if let Some(object) = self.stack_index_cursor(index) {
if index > 0 && object.offset_from(self.stack_top()) >= 0 {
nil_object()
} else {
object.value_unchecked()
}
} else {
self.pseudo_to_addr(index)
}
}
}
pub(crate) unsafe fn ensure_stack(&self, error_thread: &Thread, size: i32) -> VmErrorResult {
unsafe {
let available = self.current_call_info().top().offset_from(self.stack_top()) as i32;
if flags::LuauAutoStack.get()
&& size > 0
&& size > available
&& self.check_stack(size) == 0
{
return crate::run_error!(error_thread, "stack overflow");
}
}
Ok(())
}
pub unsafe fn abs_index(&self, index: i32) -> i32 {
unsafe {
debug_assert!(
(index > 0 && index <= self.get_top())
|| (index < 0 && -index <= self.get_top())
|| is_pseudo(index)
);
if index > 0 || is_pseudo(index) {
index
} else {
self.get_top() + index + 1
}
}
}
pub unsafe fn get_top(&self) -> i32 {
unsafe { self.stack_top().offset_from(self.stack_base()) as i32 }
}
pub unsafe fn check_stack(&self, size: i32) -> i32 {
debug_assert!(size >= 0);
unsafe {
let top_offset = self.stack_top().offset_from(self.stack_base()) as i32;
if size > LUAI_MAX_C_STACK || top_offset + size > LUAI_MAX_C_STACK {
return 0;
}
if size > 0 {
if self.stack_limit_reached(size) {
#[repr(C)]
struct GrowStackContext {
size: i32,
}
unsafe fn grow_stack_callback(
thread: &Thread,
context: &mut GrowStackContext,
) -> VmResult {
unsafe { thread.grow_stack(context.size) }?;
Ok(())
}
let mut context = GrowStackContext { size };
if self
.raw_run_protected(grow_stack_callback, &mut context)
.is_err()
{
return 0;
}
}
let pointer = self.stack_top().add(size as usize);
self.expand_stack_limit(pointer);
}
1
}
}
pub unsafe fn raw_check_stack(&self, size: i32) -> VmErrorResult {
debug_assert!(size >= 0);
unsafe {
self.check_stack_internal(size)?;
let pointer = self.stack_top().add(size as usize);
self.expand_stack_limit(pointer);
}
Ok(())
}
pub unsafe fn set_top(&self, index: i32) -> VmErrorResult {
debug_assert!(index >= 0);
unsafe {
let base = self.stack_base();
let mut top = self.stack_top();
self.ensure_stack(self, index - top.offset_from(base) as i32)?;
debug_assert!(index <= self.stack_last().offset_from(base) as i32);
let target = base.add(index as usize);
while top < target {
top.value_unchecked().set_nil();
top = top.add(1);
}
self.set_stack_top(target);
}
Ok(())
}
pub unsafe fn restore_top(&self, top: i32) {
unsafe {
let current_top = self.get_top();
debug_assert!((0..=current_top).contains(&top));
self.set_stack_top(self.stack_base().add(top as usize));
}
}
pub unsafe fn pop(&self, count: i32) {
debug_assert!(count >= 0);
unsafe {
let current_top = self.get_top();
debug_assert!(count <= current_top);
self.restore_top(current_top - count);
}
}
pub unsafe fn push_value(&self, index: i32) -> VmErrorResult {
unsafe {
self.thread_barrier();
self.ensure_stack(self, 1)?;
let top = self.stack_top();
let object = self.index_to_addr(index);
top.value_unchecked().set_obj(object);
debug_assert!(top.offset_from(self.current_call_info().top()) < 0);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn remove(&self, index: i32) {
unsafe {
let object_cursor = self.stack_index_to_cursor(index);
let next = object_cursor.add(1);
ptr::copy(
next.as_ptr(),
object_cursor.as_ptr(),
self.stack_top().offset_from(next) as usize,
);
self.set_stack_top(self.stack_top().sub(1));
}
}
pub unsafe fn insert(&self, index: i32) {
unsafe {
self.thread_barrier();
let object_cursor = self.stack_index_to_cursor(index);
let top = self.stack_top().as_ptr();
let mut slot = top;
while slot > object_cursor.as_ptr() {
ptr::copy(slot.sub(1), slot, 1);
slot = slot.sub(1);
}
ptr::copy(top, object_cursor.as_ptr(), 1);
}
}
pub unsafe fn replace(&self, index: i32) {
unsafe {
let top = self.stack_top();
debug_assert!(top.offset_from(self.stack_base()) > 0);
self.thread_barrier();
let object = self.index_to_addr(index);
debug_assert!(object != nil_object());
let value = top.sub(1);
if index == LUA_ENVIRON_INDEX {
debug_assert!(self.current_call_info() != self.base_call_info());
debug_assert!(value.value_unchecked().is_table());
let function = self.current_function();
function.set_env(value.value_unchecked().table_value());
self.barrier_value(function.into(), value.value_unchecked());
} else if index == LUA_GLOBALS_INDEX {
debug_assert!(value.value_unchecked().is_table());
self.set_globals(value.value_unchecked().table_value());
} else {
object.set_obj(value.value_unchecked());
if index < LUA_GLOBALS_INDEX {
let function = self.current_function();
self.barrier_value(function.into(), value.value_unchecked());
}
}
self.set_stack_top(top.sub(1));
}
}
pub unsafe fn x_move(&self, to: &Thread, count: i32) -> VmErrorResult {
unsafe {
debug_assert!(count >= 0);
if *self == *to {
return Ok(());
}
debug_assert!(count <= self.stack_top().offset_from(self.stack_base()) as i32);
debug_assert!(self.global() == to.global());
to.thread_barrier();
to.ensure_stack(self, count)?;
let to_top = to.stack_top();
let from_top = self.stack_top().sub(count as usize);
for index in 0..count as usize {
to_top
.add(index)
.value_unchecked()
.set_obj(from_top.add(index).value_unchecked());
}
self.set_stack_top(from_top);
to.set_stack_top(to_top.add(count as usize));
}
Ok(())
}
pub unsafe fn x_push(&self, to: &Thread, index: i32) -> VmErrorResult {
unsafe {
debug_assert!(self.global() == to.global());
to.thread_barrier();
to.ensure_stack(self, 1)?;
let top = to.stack_top();
let object = self.index_to_addr(index);
top.value_unchecked().set_obj(object);
debug_assert!(top < to.current_call_info().top());
to.set_stack_top(top.add(1));
}
Ok(())
}
}
impl Thread {
pub unsafe fn push_nil(&self) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_nil();
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_number(&self, value: f64) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_number(value);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_integer(&self, value: i32) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_number(value as f64);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_integer64(&self, value: i64) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_integer(value);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_unsigned(&self, value: u32) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_number(value as f64);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_vector(
&self,
components: [f32; crate::types::LUA_VECTOR_SIZE],
) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_vector(components);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_boolean(&self, value: i32) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_boolean(i32::from(value != 0));
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_light_userdata_tagged(&self, pointer: *mut (), tag: i32) -> VmErrorResult {
unsafe {
self.ensure_stack(self, 1)?;
let top = self.stack_top();
debug_assert!(top < self.current_call_info().top());
top.value_unchecked().set_light_userdata(pointer, tag);
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_light_userdata(&self, pointer: *mut ()) -> VmErrorResult {
unsafe { self.push_light_userdata_tagged(pointer, 0) }
}
pub unsafe fn push_string(&self, bytes: impl AsRef<[u8]>) -> VmErrorResult {
let bytes = bytes.as_ref();
unsafe {
self.check_gc()?;
self.thread_barrier();
self.ensure_stack(self, 1)?;
let interned = self.intern_string(bytes.as_bstr())?;
let top = self.stack_top();
top.value_unchecked().set_string_value(interned);
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
}
Ok(())
}
pub unsafe fn push_optional_string(&self, bytes: Option<impl AsRef<[u8]>>) -> VmErrorResult {
if let Some(bytes) = bytes {
unsafe { self.push_string(bytes) }
} else {
unsafe { self.push_nil() }
}
}
pub unsafe fn push_vfstring<'a>(
&self,
format: &str,
args: impl AsMut<[luau_printf::Arg<'a>]>,
) -> VmErrorResult<LuaString> {
unsafe {
self.check_gc()?;
self.thread_barrier();
self.push_vfstring_internal(format, args)
}
}
pub unsafe fn push_fstring<'a>(
&self,
format: &str,
args: impl AsMut<[luau_printf::Arg<'a>]>,
) -> VmErrorResult<LuaString> {
unsafe {
self.check_gc()?;
self.thread_barrier();
self.push_fstring_internal(format, args)
}
}
}
impl Thread {
unsafe fn borrow_interned_string(&self, string: TString) -> &BStr {
unsafe {
let raw = string.as_ptr().as_ref().unwrap_unchecked();
core::slice::from_raw_parts(string.data_ptr(), raw.len as usize).as_bstr()
}
}
pub unsafe fn is_number(&self, index: i32) -> i32 {
unsafe {
let object = self.index_to_addr(index);
let mut converted = RAW_TVALUE_NIL;
let converted = TValue::from_mut(&mut converted);
self.to_number_internal(object, converted).is_some() as i32
}
}
pub unsafe fn is_string(&self, index: i32) -> i32 {
let tag = unsafe { self.type_of(index) };
(tag == LUA_TSTRING || tag == LUA_TNUMBER) as i32
}
pub unsafe fn is_light_userdata(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TLIGHTUSERDATA) as i32
}
pub unsafe fn is_function(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TFUNCTION) as i32
}
pub unsafe fn is_table(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TTABLE) as i32
}
pub unsafe fn is_nil(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TNIL) as i32
}
pub unsafe fn is_boolean(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TBOOLEAN) as i32
}
pub unsafe fn is_vector(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TVECTOR) as i32
}
pub unsafe fn is_thread(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TTHREAD) as i32
}
pub unsafe fn is_buffer(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TBUFFER) as i32
}
pub unsafe fn is_none(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TNONE) as i32
}
pub unsafe fn is_none_or_nil(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } <= LUA_TNIL) as i32
}
pub unsafe fn is_class(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TCLASS) as i32
}
pub unsafe fn is_object(&self, index: i32) -> i32 {
(unsafe { self.type_of(index) } == LUA_TOBJECT) as i32
}
pub unsafe fn is_integer64(&self, index: i32) -> i32 {
let object = unsafe { self.index_to_addr(index) };
(object != nil_object() && object.is_integer()) as i32
}
pub unsafe fn is_native_function(&self, index: i32) -> i32 {
let object = unsafe { self.index_to_addr(index) };
(object != nil_object()
&& object.is_function()
&& unsafe { object.closure_value().is_native() }) as i32
}
pub unsafe fn is_lua_function(&self, index: i32) -> i32 {
let object = unsafe { self.index_to_addr(index) };
(object != nil_object()
&& object.is_function()
&& unsafe { object.closure_value().is_lua() }) as i32
}
pub unsafe fn is_userdata(&self, index: i32) -> i32 {
let object = unsafe { self.index_to_addr(index) };
(object != nil_object() && (object.is_userdata() || object.is_light_userdata())) as i32
}
pub unsafe fn type_of(&self, index: i32) -> i32 {
let object = unsafe { self.index_to_addr(index) };
if object == nil_object() {
LUA_TNONE
} else {
object.tt()
}
}
pub unsafe fn type_name(&self, tag: i32) -> LuaString {
debug_assert!((LUA_TNONE..LUA_T_COUNT as i32).contains(&tag));
if tag == LUA_TNONE {
LuaString::from_static(b"no value".as_bstr())
} else {
LuaString::from_interned(unsafe { self.global().type_name(tag as usize) })
}
}
pub unsafe fn equal(&self, left: i32, right: i32) -> VmResult<i32> {
unsafe {
let left_object = self.index_to_addr(left);
let right_object = self.index_to_addr(right);
if left_object == nil_object()
|| right_object == nil_object()
|| left_object.tt() != right_object.tt()
{
Ok(0)
} else {
self.equal_value(left_object, right_object)
}
}
}
pub unsafe fn raw_equal(&self, left: i32, right: i32) -> i32 {
let left_object = unsafe { self.index_to_addr(left) };
let right_object = unsafe { self.index_to_addr(right) };
if left_object == nil_object() || right_object == nil_object() {
0
} else {
left_object.raw_equal(right_object) as i32
}
}
pub unsafe fn less_than(&self, left: i32, right: i32) -> VmResult<i32> {
unsafe {
let left_object = self.index_to_addr(left);
let right_object = self.index_to_addr(right);
if left_object == nil_object() || right_object == nil_object() {
Ok(0)
} else {
self.less_than_internal(left_object, right_object)
}
}
}
pub unsafe fn to_number(&self, index: i32) -> Option<f64> {
unsafe {
let object = self.index_to_addr(index);
let mut converted = RAW_TVALUE_NIL;
let converted = TValue::from_mut(&mut converted);
self.to_number_internal(object, converted)
.map(|value| value.number_value())
}
}
pub unsafe fn to_integer(&self, index: i32) -> Option<i32> {
unsafe {
let object = self.index_to_addr(index);
let mut converted = RAW_TVALUE_NIL;
let converted = TValue::from_mut(&mut converted);
self.to_number_internal(object, converted)
.map(|value| value.number_value())
.map(crate::number::num_to_int)
}
}
pub unsafe fn to_unsigned(&self, index: i32) -> Option<u32> {
unsafe {
let object = self.index_to_addr(index);
let mut converted = RAW_TVALUE_NIL;
let converted = TValue::from_mut(&mut converted);
self.to_number_internal(object, converted)
.map(|value| value.number_value())
.map(crate::number::num_to_unsigned)
}
}
pub unsafe fn to_vector(&self, index: i32) -> Option<[f32; crate::types::LUA_VECTOR_SIZE]> {
let object = unsafe { self.index_to_addr(index) };
if object == nil_object() || !object.is_vector() {
None
} else {
Some(object.vector_value())
}
}
pub unsafe fn to_boolean(&self, index: i32) -> i32 {
let object = unsafe { self.index_to_addr(index) };
(!object.is_false()) as i32
}
pub unsafe fn to_integer64(&self, index: i32) -> Option<i64> {
unsafe {
let object = self.index_to_addr(index);
if object != nil_object() && object.is_integer() {
Some(object.integer_value())
} else {
None
}
}
}
pub unsafe fn to_string(&self, index: i32) -> VmErrorResult<Option<&BStr>> {
unsafe {
let mut object = self.index_to_addr(index);
if !object.is_string() {
self.thread_barrier();
if self.to_string_internal(object)? == 0 {
return Ok(None);
}
self.check_gc()?;
object = self.index_to_addr(index);
}
Ok(Some(self.borrow_interned_string(object.string_value())))
}
}
pub unsafe fn to_string_atom(&self, index: i32) -> Option<(&BStr, i32)> {
unsafe {
let object = self.index_to_addr(index);
if object == nil_object() || !object.is_string() {
return None;
}
let string = object.string_value();
self.update_atom(string);
Some((
self.borrow_interned_string(string),
string.as_ptr().as_ref().unwrap_unchecked().atom as i32,
))
}
}
pub unsafe fn namecall_atom(&self) -> Option<(&BStr, i32)> {
unsafe {
let string = self.name_call()?;
self.update_atom(string);
Some((
self.borrow_interned_string(string),
string.as_ptr().as_ref().unwrap_unchecked().atom as i32,
))
}
}
pub unsafe fn len(&self, index: i32) -> VmResult<f64> {
unsafe {
self.ensure_stack(self, 1)?;
let value = self.index_to_addr(index);
let result = self.stack_top();
self.do_len(result, value)?;
debug_assert!(result < self.current_call_info().top());
self.set_stack_top(result.add(1));
Ok(result.value_unchecked().number_value())
}
}
pub unsafe fn obj_len(&self, index: i32) -> i32 {
unsafe {
let object = self.index_to_addr(index);
if object == nil_object() {
return 0;
}
match object.tt() {
x if x == LUA_TSTRING => {
object
.string_value()
.as_ptr()
.as_ref()
.unwrap_unchecked()
.len as i32
}
x if x == LUA_TUSERDATA => {
object
.userdata_value()
.as_ptr()
.as_ref()
.unwrap_unchecked()
.len
}
x if x == LUA_TBUFFER => {
object
.buffer_value()
.as_ptr()
.as_ref()
.unwrap_unchecked()
.len as i32
}
x if x == LUA_TTABLE => object.table_value().getn(),
_ => 0,
}
}
}
pub unsafe fn to_pointer(&self, index: i32) -> *const () {
let object = unsafe { self.index_to_addr(index) };
if object == nil_object() {
return ptr::null();
}
match object.tt() {
LUA_TUSERDATA => object.userdata_value().data_ptr().cast(),
LUA_TLIGHTUSERDATA => object.pointer_value(),
_ if object.is_collectable() => object.gc_value().as_ptr().cast(),
_ => ptr::null(),
}
}
}
impl Thread {
pub unsafe fn new_buffer(&self, size: usize) -> VmErrorResult<*mut u8> {
unsafe {
self.check_gc()?;
self.thread_barrier();
self.ensure_stack(self, 1)?;
let buffer = self.new_buffer_internal(size)?;
let data = buffer.data_mut_ptr();
let top = self.stack_top();
top.value_unchecked().set_buffer_value(buffer);
debug_assert!(top < self.current_call_info().top());
self.set_stack_top(top.add(1));
Ok(data)
}
}
pub unsafe fn to_buffer(&self, index: i32) -> Option<(*mut u8, usize)> {
let object = unsafe { self.index_to_addr(index) };
if object == nil_object() || !object.is_buffer() {
return None;
}
let buffer = object.buffer_value();
Some((unsafe { buffer.data_mut_ptr() }, unsafe {
buffer.as_ptr().as_ref().unwrap_unchecked().len as usize
}))
}
}