luau_vm/thread/
userdata.rs1use core::ptr;
2
3use luau_common::{ByteSlice, flags};
4
5use crate::VmErrorResult;
6use crate::gc::GcObject;
7use crate::gc::{GcBarrier, GcRuntime};
8use crate::handle::RawHandle;
9use crate::metamethod::{MetamethodRuntime, TmEvent};
10use crate::state::{EmbedderGc, ThreadState};
11use crate::string::LuaString;
12use crate::string::StringRuntime;
13use crate::table::TableRuntime;
14use crate::userdata::UserdataRuntime;
15use crate::userdata::{
16 LIGHT_USERDATA_TAG_LIMIT, LuaDestructor, LuaInlineDestructor, LuaUserdataDirectAccess,
17 LuaUserdataDirectFieldGet, LuaUserdataDirectNamecall, LuaUserdataMark, USERDATA_TAG_LIMIT,
18};
19use crate::value::{TValue, nil_object};
20
21use super::Thread;
22use super::stack::RawStackAccess;
23
24impl Thread {
25 pub unsafe fn new_userdata_tagged(&self, size: usize, tag: i32) -> VmErrorResult<*mut ()> {
27 assert!((tag as u32) < crate::userdata::USERDATA_TAG_LIMIT as u32);
28 unsafe { self.new_userdata_tagged_internal(size, tag) }
29 }
30
31 pub unsafe fn new_userdata_tagged_with_metatable(
33 &self,
34 size: usize,
35 tag: i32,
36 ) -> VmErrorResult<*mut ()> {
37 assert!((tag as u32) < crate::userdata::USERDATA_TAG_LIMIT as u32);
38 unsafe {
39 self.check_gc()?;
40 self.thread_barrier();
41 self.ensure_stack(self, 1)?;
42
43 let userdata = self.new_userdata_internal(size, tag)?;
44 debug_assert!(!GcObject::from(userdata).is_black());
45
46 let metatable = self.global().userdata_metatable(tag as usize);
47 userdata.set_metatable(metatable);
48
49 let data = userdata.data_mut_ptr();
50 let top = self.stack_top();
51 top.value_unchecked().set_userdata_value(userdata);
52 debug_assert!(top < self.current_call_info().top());
53 self.set_stack_top(top.add(1));
54
55 Ok(data.cast())
56 }
57 }
58
59 pub unsafe fn new_userdata_dtor(
61 &self,
62 size: usize,
63 destructor: LuaInlineDestructor,
64 ) -> VmErrorResult<*mut ()> {
65 let payload_len = size.saturating_add(core::mem::size_of::<LuaInlineDestructor>());
66 unsafe {
67 self.check_gc()?;
68 self.thread_barrier();
69 self.ensure_stack(self, 1)?;
70
71 let userdata = self
72 .new_userdata_internal(payload_len, crate::userdata::USERDATA_TAG_IDTOR as i32)?;
73 userdata.set_inline_destructor(size, destructor);
74
75 let data = userdata.data_mut_ptr();
76 let top = self.stack_top();
77 top.value_unchecked().set_userdata_value(userdata);
78 debug_assert!(top < self.current_call_info().top());
79 self.set_stack_top(top.add(1));
80
81 Ok(data.cast())
82 }
83 }
84
85 pub unsafe fn new_userdata(&self, size: usize) -> VmErrorResult<*mut ()> {
87 unsafe { self.new_userdata_tagged_internal(size, 0) }
88 }
89
90 pub unsafe fn to_light_userdata(&self, index: i32) -> *mut () {
92 let object = unsafe { self.index_to_addr(index) };
93 if object == nil_object() || !object.is_light_userdata() {
94 ptr::null_mut()
95 } else {
96 object.pointer_value()
97 }
98 }
99
100 pub unsafe fn to_light_userdata_tagged(&self, index: i32, tag: i32) -> *mut () {
102 let object = unsafe { self.index_to_addr(index) };
103 if object == nil_object()
104 || !object.is_light_userdata()
105 || object.light_userdata_tag() != tag
106 {
107 ptr::null_mut()
108 } else {
109 object.pointer_value()
110 }
111 }
112
113 pub unsafe fn to_userdata(&self, index: i32) -> *mut () {
115 let object = unsafe { self.index_to_addr(index) };
116 if object == nil_object() {
117 return ptr::null_mut();
118 }
119
120 if object.is_userdata() {
121 let userdata = object.userdata_value();
122 unsafe { userdata.data_mut_ptr().cast() }
123 } else if object.is_light_userdata() {
124 object.pointer_value()
125 } else {
126 ptr::null_mut()
127 }
128 }
129
130 pub unsafe fn to_userdata_tagged(&self, index: i32, tag: i32) -> *mut () {
132 let object = unsafe { self.index_to_addr(index) };
133 if object == nil_object() || !object.is_userdata() {
134 return ptr::null_mut();
135 }
136
137 let userdata = object.userdata_value();
138 if unsafe { userdata.as_ptr().as_ref().unwrap_unchecked().tag as i32 } == tag {
139 unsafe { userdata.data_mut_ptr().cast() }
140 } else {
141 ptr::null_mut()
142 }
143 }
144
145 pub unsafe fn userdata_tag(&self, index: i32) -> i32 {
147 let object = unsafe { self.index_to_addr(index) };
148 if object == nil_object() || !object.is_userdata() {
149 -1
150 } else {
151 unsafe {
152 object
153 .userdata_value()
154 .as_ptr()
155 .as_ref()
156 .unwrap_unchecked()
157 .tag as i32
158 }
159 }
160 }
161
162 pub unsafe fn light_userdata_tag(&self, index: i32) -> i32 {
164 let object = unsafe { self.index_to_addr(index) };
165 if object == nil_object() || !object.is_light_userdata() {
166 -1
167 } else {
168 object.light_userdata_tag()
169 }
170 }
171
172 pub unsafe fn set_userdata_tag(&self, index: i32, tag: i32) {
174 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
175
176 let object = unsafe { self.to_object(index).unwrap_unchecked() };
177 debug_assert!(object.is_userdata());
178
179 unsafe {
180 object
181 .userdata_value()
182 .as_ptr()
183 .as_mut()
184 .unwrap_unchecked()
185 .tag = tag as u8
186 };
187 }
188
189 pub unsafe fn set_userdata_dtor(&self, tag: i32, destructor: LuaDestructor) {
191 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
192
193 unsafe {
194 self.global()
195 .set_userdata_dtor(tag as usize, Some(destructor))
196 };
197 }
198
199 pub unsafe fn get_userdata_dtor(&self, tag: i32) -> Option<LuaDestructor> {
201 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
202
203 unsafe { self.global().userdata_dtor(tag as usize) }
204 }
205
206 pub unsafe fn set_userdata_mark(&self, tag: i32, mark: Option<LuaUserdataMark>) {
211 debug_assert!(flags::LuauGcTraceUdata.get());
212 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
213
214 unsafe { self.global().set_userdata_mark(tag as usize, mark) };
215 }
216
217 pub unsafe fn set_embedder_gc(&self, callback: Option<EmbedderGc>) {
223 debug_assert!(flags::LuauGcTraceUdata.get());
224 unsafe { self.global().set_embedder_gc(callback) };
225 }
226
227 pub unsafe fn set_userdata_metatable(&self, tag: i32) {
229 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
230 unsafe {
231 let top_cursor = self.stack_top();
232 debug_assert!(top_cursor.offset_from(self.stack_base()) > 0);
233 debug_assert!(self.global().userdata_metatable(tag as usize).is_none());
234 let top = top_cursor.sub(1).value_unchecked();
235 debug_assert!(top.is_table());
236
237 self.global()
238 .set_userdata_metatable(tag as usize, Some(top.table_value()));
239 self.set_stack_top(top_cursor.sub(1));
240 }
241 }
242
243 pub unsafe fn get_userdata_metatable(&self, tag: i32) -> VmErrorResult {
245 unsafe {
246 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
247
248 self.thread_barrier();
249 self.ensure_stack(self, 1)?;
250
251 let top = self.stack_top();
252
253 if let Some(metatable) = self.global().userdata_metatable(tag as usize) {
254 top.value_unchecked().set_table_value(metatable);
255 } else {
256 top.value_unchecked().set_nil();
257 }
258
259 debug_assert!(top < self.current_call_info().top());
260 self.set_stack_top(top.add(1));
261 }
262 Ok(())
263 }
264
265 pub unsafe fn get_userdata_name(&self, tag: i32) -> LuaString {
267 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
268
269 unsafe {
270 let global = self.global();
271 if let Some(metatable) = global.userdata_metatable(tag as usize) {
272 let value = metatable.get_str(global.tm_name(TmEvent::Type as usize));
273 if value.is_string() {
274 return LuaString::from_interned(value.string_value());
275 }
276 }
277
278 LuaString::from_interned(global.type_name(crate::types::LUA_TUSERDATA as usize))
279 }
280 }
281
282 pub unsafe fn get_light_userdata_name(&self, tag: i32) -> Option<LuaString> {
284 debug_assert!((tag as u32) < LIGHT_USERDATA_TAG_LIMIT as u32);
285
286 unsafe {
287 self.global()
288 .light_userdata_name(tag as usize)
289 .map(LuaString::from_interned)
290 }
291 }
292
293 pub unsafe fn register_userdata_direct_access(
295 &self,
296 tag: i32,
297 get: LuaUserdataDirectAccess,
298 set: LuaUserdataDirectAccess,
299 namecall: Option<LuaUserdataDirectNamecall>,
300 ) -> i32 {
301 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
302
303 unsafe {
304 self.thread_barrier();
305
306 let global_handle = self.global();
307 let Some(table) = global_handle.userdata_metatable(tag as usize) else {
308 return 0;
309 };
310
311 let global = global_handle.as_ptr().as_mut().unwrap_unchecked();
312
313 let data = &mut global.userdata_direct[tag as usize];
314 TValue::from_mut(&mut data.index_tm).set_nil();
315 TValue::from_mut(&mut data.new_index_tm).set_nil();
316 TValue::from_mut(&mut data.name_call_tm).set_nil();
317 data.index = None;
318 data.new_index = None;
319 data.name_call = None;
320
321 if let Some(index_tm) = self.fast_tm(Some(table), TmEvent::Index) {
322 TValue::from_mut(&mut data.index_tm).set_obj(index_tm);
323 data.index = Some(get);
324 }
325
326 if let Some(new_index_tm) = self.fast_tm(Some(table), TmEvent::NewIndex) {
327 TValue::from_mut(&mut data.new_index_tm).set_obj(new_index_tm);
328 data.new_index = Some(set);
329 }
330
331 if let Some(name_call_tm) = self.fast_tm(Some(table), TmEvent::NameCall) {
332 TValue::from_mut(&mut data.name_call_tm).set_obj(name_call_tm);
333 data.name_call = namecall;
334 }
335
336 1
337 }
338 }
339
340 pub unsafe fn register_userdata_direct_field_get(
342 &self,
343 tag: i32,
344 field: &[u8],
345 get: LuaUserdataDirectFieldGet,
346 ) -> VmErrorResult {
347 unsafe {
348 if !flags::LuauDirectFieldGet.get() {
349 return Ok(());
350 }
351
352 debug_assert!((tag as u32) < USERDATA_TAG_LIMIT as u32);
353
354 let global = self.global();
355 if global.userdata_direct_field(tag as usize).is_none() {
356 let table = self.new_table_internal(0, 1)?;
357 global.set_userdata_direct_field(tag as usize, Some(table));
358 }
359
360 let string = self.intern_string(field.as_bstr())?;
361 string.fix();
362
363 let table = global
364 .userdata_direct_field(tag as usize)
365 .unwrap_unchecked();
366 let node_cursor = self.set_str(table, string)?;
367 node_cursor
368 .node_unchecked()
369 .value_unchecked()
370 .set_light_userdata(get as *const () as *mut (), 0);
371 }
372 Ok(())
373 }
374
375 pub unsafe fn set_light_userdata_name(&self, tag: i32, name: &str) -> VmErrorResult {
377 unsafe {
378 debug_assert!((tag as u32) < LIGHT_USERDATA_TAG_LIMIT as u32);
379 debug_assert!(self.global().light_userdata_name(tag as usize).is_none());
380
381 let global = self.global();
382 if global.light_userdata_name(tag as usize).is_none() {
383 let string = self.intern_string(name.as_bytes().as_bstr())?;
384 string.fix();
385 global.set_light_userdata_name(tag as usize, Some(string));
386 }
387 }
388 Ok(())
389 }
390}