luau_vm/function/
closure.rs1use core::mem::{offset_of, size_of};
2use core::ptr::{self, NonNull};
3
4use crate::Table;
5use crate::gc::GcObject;
6use crate::gc::RawGcObject;
7use crate::handle::RawHandle;
8use crate::native::{RawNativeContinuation, RawNativeFunction};
9use crate::string::{LuaString, RawTString, TString};
10use crate::table::RawLuaTable;
11use crate::value::{RawTValue, TValue, TValueCursor};
12
13use super::{Proto, RawProto};
14
15#[repr(C)]
16pub struct RawClosure {
17 pub tt: u8,
18 pub marked: u8,
19 pub memcat: u8,
20 pub is_native: u8,
21 pub n_upvalues: u8,
22 pub stack_size: u8,
23 pub preload: u8,
24 pub gc_list: *mut RawGcObject,
25 pub env: *mut RawLuaTable,
26 pub data: [u8; 0],
27}
28
29#[repr(C)]
30#[derive(Clone, Copy)]
31pub struct RawNativeClosure {
32 pub function: Option<RawNativeFunction>,
33 pub continuation: Option<RawNativeContinuation>,
34 pub debug_name_deprecated: Option<&'static str>,
35 pub debug_name: *mut RawTString,
36}
37
38#[repr(C)]
39#[derive(Clone, Copy)]
40pub struct RawLuaClosure {
41 pub proto: *mut RawProto,
42}
43
44#[derive(Clone, Copy, PartialEq, Eq)]
45#[repr(transparent)]
46pub struct Closure {
54 raw: NonNull<RawClosure>,
55}
56
57#[allow(
58 clippy::missing_safety_doc,
59 reason = "Closure's shared raw-handle contract is documented on Closure"
60)]
61impl Closure {
62 pub const unsafe fn from_raw(raw: NonNull<RawClosure>) -> Self {
63 Self { raw }
64 }
65
66 pub fn size_native_closure(n: usize) -> usize {
67 offset_of!(RawClosure, data) + size_of::<RawNativeClosure>() + size_of::<RawTValue>() * n
68 }
69
70 pub fn size_lua_closure(n: usize) -> usize {
71 offset_of!(RawClosure, data) + size_of::<RawLuaClosure>() + size_of::<RawTValue>() * n
72 }
73
74 pub unsafe fn size(&self) -> usize {
76 unsafe {
77 if self.is_native() {
78 Closure::size_native_closure(
79 self.as_ptr().as_ref().unwrap_unchecked().n_upvalues as usize,
80 )
81 } else {
82 Closure::size_lua_closure(
83 self.as_ptr().as_ref().unwrap_unchecked().n_upvalues as usize,
84 )
85 }
86 }
87 }
88
89 pub unsafe fn is_native(&self) -> bool {
90 unsafe { self.as_ptr().as_ref().unwrap_unchecked().is_native != 0 }
91 }
92
93 pub unsafe fn is_lua(&self) -> bool {
94 unsafe { !self.is_native() }
95 }
96
97 pub unsafe fn proto(&self) -> Option<Proto> {
98 unsafe {
99 if self.is_lua() {
100 NonNull::new(self.lua_data().proto).map(|raw| Proto::from_raw(raw))
101 } else {
102 None
103 }
104 }
105 }
106
107 pub unsafe fn env(&self) -> Table {
108 unsafe {
109 Table::from_raw(NonNull::new_unchecked(
110 self.as_ptr().as_ref().unwrap_unchecked().env,
111 ))
112 }
113 }
114
115 pub unsafe fn gc_list(&self) -> Option<GcObject> {
116 unsafe {
117 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().gc_list)
118 .map(|raw| GcObject::from_raw(raw))
119 }
120 }
121
122 pub unsafe fn set_gc_list(&self, gc_list: Option<GcObject>) {
123 unsafe {
124 self.as_ptr().as_mut().unwrap_unchecked().gc_list =
125 gc_list.map_or(ptr::null_mut(), |object| object.as_ptr());
126 }
127 }
128
129 pub unsafe fn set_env(&self, env: Table) {
130 unsafe {
131 self.as_ptr().as_mut().unwrap_unchecked().env = env.as_ptr();
132 }
133 }
134
135 pub unsafe fn native_data(&self) -> RawNativeClosure {
136 debug_assert!(unsafe { self.is_native() });
137 unsafe { self.data_ptr().cast::<RawNativeClosure>().read() }
138 }
139
140 pub unsafe fn native_data_ptr(&self) -> *mut RawNativeClosure {
141 debug_assert!(unsafe { self.is_native() });
142 self.data_ptr().cast()
143 }
144
145 pub unsafe fn set_native_data(&self, data: RawNativeClosure) {
146 unsafe { self.native_data_ptr().write(data) };
147 }
148
149 pub unsafe fn native_debug_name(&self) -> Option<LuaString> {
150 debug_assert!(unsafe { self.is_native() });
151 let data = unsafe { self.native_data() };
152 if luau_common::flags::LuauManagedDebugNames.get() {
153 NonNull::new(data.debug_name)
154 .map(|raw| LuaString::from_interned(unsafe { TString::from_raw(raw) }))
155 } else {
156 data.debug_name_deprecated
157 .map(|name| LuaString::from_static(luau_common::BStr::new(name.as_bytes())))
158 }
159 }
160
161 pub unsafe fn lua_data(&self) -> RawLuaClosure {
162 debug_assert!(unsafe { self.is_lua() });
163 unsafe { self.data_ptr().cast::<RawLuaClosure>().read() }
164 }
165
166 pub unsafe fn lua_data_ptr(&self) -> *mut RawLuaClosure {
167 debug_assert!(unsafe { self.is_lua() });
168 self.data_ptr().cast()
169 }
170
171 pub unsafe fn set_lua_proto(&self, proto: Proto) {
172 unsafe {
173 self.lua_data_ptr().write(RawLuaClosure {
174 proto: proto.as_ptr(),
175 })
176 };
177 }
178
179 pub unsafe fn native_upvalue(&self, index: usize) -> TValue {
180 unsafe {
181 debug_assert!(index < self.as_ptr().as_ref().unwrap_unchecked().n_upvalues as usize);
182 TValue::from_raw(NonNull::new_unchecked(
183 self.native_upvalues_ptr().add(index),
184 ))
185 }
186 }
187
188 pub unsafe fn native_upvalues(&self) -> TValueCursor {
189 unsafe { TValueCursor::from_ptr(self.native_upvalues_ptr()) }
190 }
191
192 pub unsafe fn lua_upvalue_ref(&self, index: usize) -> TValue {
193 unsafe {
194 debug_assert!(index < self.as_ptr().as_ref().unwrap_unchecked().n_upvalues as usize);
195 TValue::from_raw(NonNull::new_unchecked(
196 self.lua_upvalue_refs_ptr().add(index),
197 ))
198 }
199 }
200
201 pub unsafe fn lua_upvalue_refs(&self) -> TValueCursor {
202 unsafe { TValueCursor::from_ptr(self.lua_upvalue_refs_ptr()) }
203 }
204
205 fn data_ptr(&self) -> *mut u8 {
206 unsafe { self.as_ptr().cast::<u8>().add(offset_of!(RawClosure, data)) }
207 }
208
209 unsafe fn native_upvalues_ptr(&self) -> *mut RawTValue {
210 debug_assert!(unsafe { self.is_native() });
211 unsafe {
212 self.data_ptr()
213 .add(size_of::<RawNativeClosure>())
214 .cast::<RawTValue>()
215 }
216 }
217
218 unsafe fn lua_upvalue_refs_ptr(&self) -> *mut RawTValue {
219 debug_assert!(unsafe { self.is_lua() });
220 unsafe {
221 self.data_ptr()
222 .add(size_of::<RawLuaClosure>())
223 .cast::<RawTValue>()
224 }
225 }
226}
227impl crate::handle::sealed::Sealed for Closure {}
228impl RawHandle for Closure {
229 type Raw = RawClosure;
230
231 fn as_ptr(&self) -> *mut Self::Raw {
232 self.raw.as_ptr()
233 }
234}
235
236impl AsRef<Closure> for Closure {
237 fn as_ref(&self) -> &Closure {
238 self
239 }
240}