1use core::mem::size_of;
2use core::ptr::{self, NonNull};
3
4use luau_common::{ByteSlice, flags};
5
6use crate::call::ThreadStack;
7use crate::debug::DebugRuntime;
8use crate::function::{FunctionRuntime, RawNativeClosure};
9use crate::gc::GcBarrier;
10use crate::gc::{GcObject, RawGcObject};
11use crate::handle::RawHandle;
12use crate::handle::sealed::Sealed;
13use crate::memory::{LuaPage, MemoryRuntime};
14use crate::metamethod::{TM_N, TmEvent};
15use crate::native::{NativeCallContext, NativeCallResult};
16use crate::state::ThreadState;
17use crate::string::{RawTString, StringRuntime, TString};
18use crate::table::{RawLuaTable, TableRuntime};
19use crate::thread::Thread;
20use crate::types::{LUA_TCLASS, LUA_TOBJECT};
21use crate::value::{RAW_TVALUE_NIL, RawTValue, TValue};
22use crate::vm::VmOperations;
23use crate::{Table, VmErrorResult};
24
25#[repr(C)]
26pub struct RawLuauClass {
27 pub tt: u8,
28 pub marked: u8,
29 pub memcat: u8,
30 pub gc_list: *mut RawGcObject,
31 pub name: *mut RawTString,
32 pub static_members: *mut RawTValue,
33 pub members_to_offset: *mut RawLuaTable,
34 pub offset_to_member: *mut *mut RawTString,
35 pub metatable: *mut RawLuaTable,
36 pub instance_metatable: *mut RawLuaTable,
37 pub number_of_instance_members: u32,
38 pub number_of_all_members: u32,
39}
40
41#[repr(C)]
42pub struct RawLuauObject {
43 pub tt: u8,
44 pub marked: u8,
45 pub memcat: u8,
46 pub gc_list: *mut RawGcObject,
47 pub class: *mut RawLuauClass,
48 pub number_of_members: u32,
49 pub members: *mut RawTValue,
50}
51
52#[derive(Clone, Copy, PartialEq, Eq)]
53#[repr(transparent)]
54pub struct Class {
62 pub(crate) raw: NonNull<RawLuauClass>,
63}
64
65#[allow(
73 clippy::missing_safety_doc,
74 reason = "all methods share the capability-level safety contract"
75)]
76pub trait ClassRuntime: Sealed {
77 unsafe fn new_blank_class(&self, name: TString) -> VmErrorResult<Class>;
79
80 unsafe fn add_class_metatable(&self, class: Class) -> VmErrorResult;
82
83 unsafe fn new_class(
85 &self,
86 name: TString,
87 members_to_offset: Table,
88 offset_to_member: *mut TString,
89 number_of_instance_members: u32,
90 number_of_static_members: u32,
91 ) -> VmErrorResult<Class>;
92
93 unsafe fn inherit_class(&self, child: Class, parent: Class) -> VmErrorResult<Class>;
95
96 unsafe fn free_class(&self, class: Class, page: LuaPage);
98
99 unsafe fn free_object(&self, object: Object, page: LuaPage);
101
102 unsafe fn create_object(&self) -> NativeCallResult;
104}
105
106#[allow(
107 clippy::missing_safety_doc,
108 reason = "Class's shared raw-handle contract is documented on Class"
109)]
110impl Class {
111 pub const unsafe fn from_raw(raw: NonNull<RawLuauClass>) -> Self {
112 Self { raw }
113 }
114
115 pub unsafe fn name(&self) -> TString {
116 unsafe {
117 TString::from_raw(NonNull::new_unchecked(
118 self.as_ptr().as_ref().unwrap_unchecked().name,
119 ))
120 }
121 }
122
123 pub unsafe fn members_to_offset(&self) -> Table {
124 unsafe {
125 Table::from_raw(NonNull::new_unchecked(
126 self.as_ptr().as_ref().unwrap_unchecked().members_to_offset,
127 ))
128 }
129 }
130
131 pub unsafe fn offset_to_member(&self, index: usize) -> TString {
132 unsafe {
133 TString::from_raw(NonNull::new_unchecked(
134 *self
135 .as_ptr()
136 .as_ref()
137 .unwrap_unchecked()
138 .offset_to_member
139 .add(index),
140 ))
141 }
142 }
143
144 pub unsafe fn static_member(&self, index: usize) -> TValue {
145 unsafe {
146 TValue::from_raw(NonNull::new_unchecked(
147 self.as_ptr()
148 .as_ref()
149 .unwrap_unchecked()
150 .static_members
151 .add(index),
152 ))
153 }
154 }
155
156 pub fn static_member_count(&self) -> usize {
157 let class = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
158 (class.number_of_all_members - class.number_of_instance_members) as usize
159 }
160
161 pub fn member_count(&self) -> usize {
162 unsafe {
163 self.as_ptr()
164 .as_ref()
165 .unwrap_unchecked()
166 .number_of_all_members as usize
167 }
168 }
169
170 pub unsafe fn allocation_size(&self) -> usize {
171 size_of::<RawLuauClass>()
172 + self.static_member_count() * size_of::<RawTValue>()
173 + self.member_count() * size_of::<*mut RawTString>()
174 }
175
176 pub unsafe fn gc_list(&self) -> Option<GcObject> {
177 unsafe {
178 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().gc_list)
179 .map(|raw| GcObject::from_raw(raw))
180 }
181 }
182
183 pub unsafe fn set_gc_list(&self, gc_list: Option<GcObject>) {
184 unsafe {
185 self.as_ptr().as_mut().unwrap_unchecked().gc_list =
186 gc_list.map_or(ptr::null_mut(), |object| object.as_ptr());
187 }
188 }
189
190 pub unsafe fn metatable(&self) -> Option<Table> {
191 unsafe {
192 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().metatable)
193 .map(|raw| Table::from_raw(raw))
194 }
195 }
196
197 pub unsafe fn set_metatable(&self, metatable: Option<Table>) {
198 unsafe {
199 self.as_ptr().as_mut().unwrap_unchecked().metatable =
200 metatable.map_or(core::ptr::null_mut(), |table| table.as_ptr());
201 }
202 }
203
204 pub unsafe fn instance_metatable(&self) -> Option<Table> {
205 unsafe {
206 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().instance_metatable)
207 .map(|raw| Table::from_raw(raw))
208 }
209 }
210
211 pub unsafe fn set_instance_metatable(&self, metatable: Option<Table>) {
212 unsafe {
213 self.as_ptr().as_mut().unwrap_unchecked().instance_metatable =
214 metatable.map_or(core::ptr::null_mut(), |table| table.as_ptr());
215 }
216 }
217
218 pub unsafe fn member_offset(&self, key: TValue) -> Option<u32> {
219 let offset = unsafe { self.members_to_offset().get(key) };
220 if offset.is_nil() {
221 None
222 } else {
223 debug_assert!(offset.is_number());
224 Some(offset.number_value() as u32)
225 }
226 }
227
228 pub unsafe fn member_offset_str(&self, key: TString) -> Option<u32> {
229 let offset = unsafe { self.members_to_offset().get_str(key) };
230 if offset.is_nil() {
231 None
232 } else {
233 debug_assert!(offset.is_number());
234 Some(offset.number_value() as u32)
235 }
236 }
237
238 pub unsafe fn lookup_static_member(&self, key: TValue) -> Option<TValue> {
239 unsafe {
240 let offset = self.member_offset(key)?;
241 if offset
242 < self
243 .as_ptr()
244 .as_ref()
245 .unwrap_unchecked()
246 .number_of_instance_members
247 {
248 None
249 } else {
250 Some(
251 self.static_member(
252 (offset
253 - self
254 .as_ptr()
255 .as_ref()
256 .unwrap_unchecked()
257 .number_of_instance_members) as usize,
258 ),
259 )
260 }
261 }
262 }
263
264 pub unsafe fn add_class_member(
266 &self,
267 thread: &Thread,
268 name: TString,
269 method: TValue,
270 ) -> VmErrorResult {
271 unsafe {
272 let Some(offset) = self.member_offset_str(name) else {
273 return Ok(());
274 };
275 debug_assert!(
276 offset
277 >= self
278 .as_ptr()
279 .as_ref()
280 .unwrap_unchecked()
281 .number_of_instance_members
282 && offset
283 < self
284 .as_ptr()
285 .as_ref()
286 .unwrap_unchecked()
287 .number_of_all_members
288 );
289 debug_assert!(method.is_function());
290
291 let static_member = self.static_member(
292 (offset
293 - self
294 .as_ptr()
295 .as_ref()
296 .unwrap_unchecked()
297 .number_of_instance_members) as usize,
298 );
299 static_member.set_obj(method);
300
301 let class_object: GcObject = (*self).into();
302 if method.is_collectable() && class_object.is_black() {
303 let child = method.gc_value();
304 if child.is_white() {
305 thread.barrier_forward(class_object, child);
306 }
307 }
308
309 let global = thread.global();
310 let mut is_metamethod = name == thread.intern_string(b"__tostring".as_bstr())?;
311 for index in 0..TM_N {
312 if is_metamethod {
313 break;
314 }
315
316 is_metamethod = global.tm_name(index) == name;
317 }
318
319 if is_metamethod {
320 let instance_metatable = match self.instance_metatable() {
321 Some(table) => table,
322 None => {
323 let table = thread.new_table_internal(0, 1)?;
324 self.set_instance_metatable(Some(table));
325
326 let table_object: GcObject = table.into();
327 if class_object.is_black() && table_object.is_white() {
328 thread.barrier_forward(class_object, table_object);
329 }
330
331 table
332 }
333 };
334
335 let node_cursor = thread.set_str(instance_metatable, name)?;
336 node_cursor
337 .node_unchecked()
338 .value_unchecked()
339 .set_obj(method);
340
341 let table_object: GcObject = instance_metatable.into();
342 if method.is_collectable() && table_object.is_black() {
343 let child = method.gc_value();
344 if child.is_white() {
345 thread.barrier_forward(table_object, child);
346 }
347 }
348 }
349 }
350 Ok(())
351 }
352
353 unsafe fn register_static_member(
355 &self,
356 thread: &Thread,
357 name: TString,
358 value: TValue,
359 offset: u32,
360 static_offset: u32,
361 ) -> VmErrorResult {
362 unsafe {
363 let static_member = self.static_member(static_offset as usize);
364 static_member.set_obj(value);
365
366 let class_object: GcObject = (*self).into();
367 if value.is_collectable() && class_object.is_black() {
368 let child = value.gc_value();
369 if child.is_white() {
370 thread.barrier_forward(class_object, child);
371 }
372 }
373
374 *self
375 .as_ptr()
376 .as_ref()
377 .unwrap_unchecked()
378 .offset_to_member
379 .add(offset as usize) = name.as_ptr();
380
381 let members_to_offset = self.members_to_offset();
382 let node = thread.set_str(members_to_offset, name)?;
383 node.node_unchecked()
384 .value_unchecked()
385 .set_number(offset as f64);
386 }
387
388 Ok(())
389 }
390}
391
392impl crate::handle::sealed::Sealed for Class {}
393
394impl RawHandle for Class {
395 type Raw = RawLuauClass;
396
397 fn as_ptr(&self) -> *mut Self::Raw {
398 self.raw.as_ptr()
399 }
400}
401
402impl AsRef<Class> for Class {
403 fn as_ref(&self) -> &Class {
404 self
405 }
406}
407
408#[derive(Clone, Copy, PartialEq, Eq)]
409#[repr(transparent)]
410pub struct Object {
418 pub(crate) raw: NonNull<RawLuauObject>,
419}
420
421#[derive(Clone, Copy)]
422pub enum ObjectMemberLookup {
423 Cached(TValue),
424 Resolved { offset: u32, member: TValue },
425 Missing,
426}
427
428#[allow(
429 clippy::missing_safety_doc,
430 reason = "Object's shared raw-handle contract is documented on Object"
431)]
432impl Object {
433 pub const unsafe fn from_raw(raw: NonNull<RawLuauObject>) -> Self {
434 Self { raw }
435 }
436
437 pub unsafe fn class(&self) -> Class {
438 unsafe {
439 Class::from_raw(NonNull::new_unchecked(
440 self.as_ptr().as_ref().unwrap_unchecked().class,
441 ))
442 }
443 }
444
445 pub unsafe fn member(&self, index: usize) -> TValue {
446 unsafe {
447 TValue::from_raw(NonNull::new_unchecked(
448 self.as_ptr().as_ref().unwrap_unchecked().members.add(index),
449 ))
450 }
451 }
452
453 pub unsafe fn gc_list(&self) -> Option<GcObject> {
454 unsafe {
455 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().gc_list)
456 .map(|raw| GcObject::from_raw(raw))
457 }
458 }
459
460 pub unsafe fn set_gc_list(&self, gc_list: Option<GcObject>) {
461 unsafe {
462 self.as_ptr().as_mut().unwrap_unchecked().gc_list =
463 gc_list.map_or(ptr::null_mut(), |object| object.as_ptr());
464 }
465 }
466
467 pub unsafe fn gc_list_slot(&self) -> *mut *mut RawGcObject {
468 unsafe { &mut self.as_ptr().as_mut().unwrap_unchecked().gc_list }
469 }
470
471 pub unsafe fn allocation_size(&self) -> usize {
472 unsafe {
473 size_of::<RawLuauObject>()
474 + self.as_ptr().as_ref().unwrap_unchecked().number_of_members as usize
475 * size_of::<RawTValue>()
476 }
477 }
478
479 pub fn offset_in_bounds(&self, offset: u32) -> bool {
481 let class = unsafe { self.class() };
482 offset
483 < unsafe {
484 class
485 .as_ptr()
486 .as_ref()
487 .unwrap_unchecked()
488 .number_of_all_members
489 }
490 }
491
492 pub unsafe fn lookup_member_at_offset(&self, offset: u32) -> TValue {
494 debug_assert!(self.offset_in_bounds(offset));
495
496 unsafe {
497 let class = self.class();
498
499 if offset
500 < class
501 .as_ptr()
502 .as_ref()
503 .unwrap_unchecked()
504 .number_of_instance_members
505 {
506 self.member(offset as usize)
507 } else {
508 class.static_member(
509 (offset
510 - class
511 .as_ptr()
512 .as_ref()
513 .unwrap_unchecked()
514 .number_of_instance_members) as usize,
515 )
516 }
517 }
518 }
519
520 pub unsafe fn lookup_member(&self, key: TValue) -> Option<TValue> {
521 unsafe {
522 self.class()
523 .member_offset(key)
524 .map(|offset| self.lookup_member_at_offset(offset))
525 }
526 }
527
528 pub unsafe fn lookup_member_cached(&self, key: TString, slot: u8) -> ObjectMemberLookup {
529 unsafe {
530 let class = self.class();
531 let slot_offset = u32::from(slot);
532 if slot_offset
533 < class
534 .as_ptr()
535 .as_ref()
536 .unwrap_unchecked()
537 .number_of_all_members
538 && class.offset_to_member(slot as usize) == key
539 {
540 return ObjectMemberLookup::Cached(self.lookup_member_at_offset(slot_offset));
541 }
542
543 match class.member_offset_str(key) {
544 Some(offset) => ObjectMemberLookup::Resolved {
545 offset,
546 member: self.lookup_member_at_offset(offset),
547 },
548 None => ObjectMemberLookup::Missing,
549 }
550 }
551 }
552}
553
554impl crate::handle::sealed::Sealed for Object {}
555
556impl RawHandle for Object {
557 type Raw = RawLuauObject;
558
559 fn as_ptr(&self) -> *mut Self::Raw {
560 self.raw.as_ptr()
561 }
562}
563
564impl AsRef<Object> for Object {
565 fn as_ref(&self) -> &Object {
566 self
567 }
568}
569
570fn create_object_callback(ctx: NativeCallContext) -> NativeCallResult {
571 let thread = ctx.raw_thread();
572 unsafe { thread.create_object() }
573}
574
575impl ClassRuntime for Thread {
576 unsafe fn new_blank_class(&self, name: TString) -> VmErrorResult<Class> {
578 unsafe {
579 let class = self.new_gco::<Class>(
580 core::mem::size_of::<RawLuauClass>(),
581 self.as_ptr().as_ref().unwrap_unchecked().active_memcat,
582 )?;
583 GcObject::from(class).init_header(self, LUA_TCLASS as u8);
584
585 let class_ref = class.as_ptr().as_mut().unwrap_unchecked();
586 class_ref.gc_list = ptr::null_mut();
587 class_ref.name = name.as_ptr();
588 class_ref.static_members = ptr::null_mut();
589 class_ref.members_to_offset = ptr::null_mut();
590 class_ref.offset_to_member = ptr::null_mut();
591 class_ref.metatable = ptr::null_mut();
592 class_ref.instance_metatable = ptr::null_mut();
593 class_ref.number_of_instance_members = 0;
594 class_ref.number_of_all_members = 0;
595
596 Ok(class)
597 }
598 }
599
600 unsafe fn add_class_metatable(&self, class: Class) -> VmErrorResult {
602 unsafe {
603 let metatable = self.new_table_internal(0, 1)?;
604 let global_env = self.globals();
605 let constructor = self.new_native_closure(0, Some(global_env))?;
606 let debug_name = if flags::LuauManagedDebugNames.get() {
607 self.intern_string(b"luaR_createobject".as_bstr())?.as_ptr()
608 } else {
609 ptr::null_mut()
610 };
611 constructor.set_native_data(RawNativeClosure {
612 function: Some(create_object_callback),
613 continuation: None,
614 debug_name_deprecated: (!flags::LuauManagedDebugNames.get())
615 .then_some("luaR_createobject"),
616 debug_name,
617 });
618
619 let call_name = self.global().tm_name(TmEvent::Call as usize);
620 let node_cursor = self.set_str(metatable, call_name)?;
621 node_cursor
622 .node_unchecked()
623 .value_unchecked()
624 .set_closure_value(constructor);
625 metatable.as_ptr().as_mut().unwrap_unchecked().readonly = 1;
626 class.set_metatable(Some(metatable));
627
628 Ok(())
629 }
630 }
631
632 unsafe fn new_class(
634 &self,
635 name: TString,
636 members_to_offset: Table,
637 offset_to_member: *mut TString,
638 number_of_instance_members: u32,
639 number_of_static_members: u32,
640 ) -> VmErrorResult<Class> {
641 unsafe {
642 debug_assert_eq!(
643 self.global()
644 .as_ptr()
645 .as_ref()
646 .unwrap_unchecked()
647 .gc_threshold,
648 usize::MAX
649 );
650
651 let class = self.new_blank_class(name)?;
652
653 let static_members = self.new_array::<RawTValue>(
654 number_of_static_members as usize,
655 class.as_ptr().as_ref().unwrap_unchecked().memcat,
656 )?;
657
658 let class_ref = class.as_ptr().as_mut().unwrap_unchecked();
659 class_ref.static_members = static_members;
660 class_ref.members_to_offset = members_to_offset.as_ptr();
661 class_ref.offset_to_member = offset_to_member.cast();
662 class_ref.number_of_instance_members = number_of_instance_members;
663 class_ref.number_of_all_members = number_of_instance_members + number_of_static_members;
664
665 for index in 0..number_of_static_members as usize {
666 class.static_member(index).set_nil();
667 }
668
669 self.add_class_metatable(class)?;
670
671 Ok(class)
672 }
673 }
674
675 unsafe fn inherit_class(&self, child: Class, parent: Class) -> VmErrorResult<Class> {
677 unsafe {
678 let child_instance_members = child
679 .as_ptr()
680 .as_ref()
681 .unwrap_unchecked()
682 .number_of_instance_members;
683 let child_members = child
684 .as_ptr()
685 .as_ref()
686 .unwrap_unchecked()
687 .number_of_all_members;
688 let parent_instance_members = parent
689 .as_ptr()
690 .as_ref()
691 .unwrap_unchecked()
692 .number_of_instance_members;
693 let parent_members = parent
694 .as_ptr()
695 .as_ref()
696 .unwrap_unchecked()
697 .number_of_all_members;
698
699 for index in 0..parent_instance_members {
700 let name = parent.offset_to_member(index as usize);
701 if child.member_offset_str(name).is_some() {
702 let parent_name = parent.name();
703 let child_name = child.name();
704 return crate::run_error!(
705 self,
706 "Cannot override instance member '%s' of parent class '%s' in child class '%s'",
707 name.as_bytes(),
708 parent_name.as_bytes(),
709 child_name.as_bytes(),
710 );
711 }
712 }
713
714 let class = self.new_blank_class(child.name())?;
715
716 let mut inherited_static_members = 0u32;
717 for index in parent_instance_members..parent_members {
718 let name = parent.offset_to_member(index as usize);
719 if child.member_offset_str(name).is_none() {
720 inherited_static_members += 1;
721 }
722 }
723
724 let member_count = child_members + parent_instance_members + inherited_static_members;
725 let memcat = class.as_ptr().as_ref().unwrap_unchecked().memcat;
726 let offset_to_member =
727 self.new_array::<*mut RawTString>(member_count as usize, memcat)?;
728 let members_to_offset = self.new_table_internal(0, member_count as i32)?;
729
730 {
731 let class_ref = class.as_ptr().as_mut().unwrap_unchecked();
732 class_ref.offset_to_member = offset_to_member;
733 class_ref.members_to_offset = members_to_offset.as_ptr();
734 class_ref.number_of_all_members = member_count;
735 }
736
737 let mut offset = 0u32;
738 for index in 0..parent_instance_members {
739 let name = parent.offset_to_member(index as usize);
740 *offset_to_member.add(offset as usize) = name.as_ptr();
741 let value = self.set_str(members_to_offset, name)?;
742 value
743 .node_unchecked()
744 .value_unchecked()
745 .set_number(offset as f64);
746 offset += 1;
747 }
748
749 for index in 0..child_instance_members {
750 let name = child.offset_to_member(index as usize);
751 *offset_to_member.add(offset as usize) = name.as_ptr();
752 let value = self.set_str(members_to_offset, name)?;
753 value
754 .node_unchecked()
755 .value_unchecked()
756 .set_number(offset as f64);
757 offset += 1;
758 }
759
760 let static_members =
761 self.new_array::<RawTValue>((member_count - offset) as usize, memcat)?;
762 {
763 let class_ref = class.as_ptr().as_mut().unwrap_unchecked();
764 class_ref.static_members = static_members;
765 class_ref.number_of_instance_members = offset;
766 }
767
768 let mut static_offset = 0u32;
769 for index in parent_instance_members..parent_members {
770 let name = parent.offset_to_member(index as usize);
771 if child.member_offset_str(name).is_none() {
772 let value = parent.static_member((index - parent_instance_members) as usize);
773 class.register_static_member(self, name, value, offset, static_offset)?;
774 offset += 1;
775 static_offset += 1;
776 }
777 }
778
779 for index in child_instance_members..child_members {
780 let name = child.offset_to_member(index as usize);
781 let value = child.static_member((index - child_instance_members) as usize);
782 class.register_static_member(self, name, value, offset, static_offset)?;
783 offset += 1;
784 static_offset += 1;
785 }
786
787 debug_assert_eq!(offset, member_count);
788
789 self.add_class_metatable(class)?;
790
791 if let Some(instance_metatable) = parent.instance_metatable() {
792 let cloned = self.clone_table_internal(instance_metatable)?;
793 class.set_instance_metatable(Some(cloned));
794 }
795
796 Ok(class)
797 }
798 }
799 unsafe fn free_class(&self, class: Class, page: LuaPage) {
801 unsafe {
802 let class_ref = class.as_ptr().as_ref().unwrap_unchecked();
803 let memcat = class_ref.memcat;
804 let static_members = class_ref.static_members;
805 let static_member_count =
806 (class_ref.number_of_all_members - class_ref.number_of_instance_members) as usize;
807 let offset_to_member = class_ref.offset_to_member;
808 let total_members = class_ref.number_of_all_members as usize;
809 if !static_members.is_null() {
810 self.free_array(static_members, static_member_count, memcat);
811 }
812 if !offset_to_member.is_null() {
813 self.free_array(offset_to_member, total_members, memcat);
814 }
815 self.free_gco(
816 class.into(),
817 core::mem::size_of::<RawLuauClass>(),
818 memcat,
819 page,
820 );
821 }
822 }
823
824 unsafe fn free_object(&self, object: Object, page: LuaPage) {
826 unsafe {
827 let object_ref = object.as_ptr().as_ref().unwrap_unchecked();
828 let members = object_ref.members;
829 let number_of_members = object_ref.number_of_members as usize;
830 let memcat = object_ref.memcat;
831 self.free_array(members, number_of_members, memcat);
832 self.free_gco(
833 object.into(),
834 core::mem::size_of::<RawLuauObject>(),
835 memcat,
836 page,
837 );
838 }
839 }
840 unsafe fn create_object(&self) -> NativeCallResult {
842 unsafe {
843 self.check_type(1, LUA_TCLASS)?;
844
845 let class = self.stack_base().value_unchecked().class_value();
846 let num_args = self.get_top();
847 let active_memcat = self.as_ptr().as_ref().unwrap_unchecked().active_memcat;
848
849 let object =
850 self.new_gco::<Object>(core::mem::size_of::<RawLuauObject>(), active_memcat)?;
851 let object_gco: GcObject = object.into();
852 object_gco.init_header(self, LUA_TOBJECT as u8);
853
854 let member_count = class
855 .as_ptr()
856 .as_ref()
857 .unwrap_unchecked()
858 .number_of_instance_members as usize;
859 let members = self.new_array::<RawTValue>(member_count, active_memcat)?;
860
861 let object_ref = object.as_ptr().as_mut().unwrap_unchecked();
862 object_ref.gc_list = core::ptr::null_mut();
863 object_ref.class = class.as_ptr();
864 object_ref.number_of_members = class
865 .as_ptr()
866 .as_ref()
867 .unwrap_unchecked()
868 .number_of_instance_members;
869 object_ref.members = members;
870
871 for index in 0..member_count {
872 object.member(index).set_nil();
873 }
874
875 let object_slot = self.stack_top();
876 object_slot.value_unchecked().set_object_value(object);
877 self.set_stack_top(object_slot.add(1));
878
879 let value_slot = self.stack_top();
880 value_slot.value_unchecked().set_nil();
881 self.set_stack_top(value_slot.add(1));
882 let value_slot_offset = self.save_stack(value_slot);
883
884 match num_args {
885 1 => {}
886 2 => {
887 for index in 0..member_count {
888 let mut key_storage = RAW_TVALUE_NIL;
889 let key = TValue::from_mut(&mut key_storage);
890 key.set_string_value(class.offset_to_member(index));
891 let indexed_value = self.stack_base().add(1).value_unchecked();
892 let value_slot = self.restore_stack(value_slot_offset);
893
894 self.get_table_internal(indexed_value, key, value_slot)?;
895 let value_slot = self.restore_stack(value_slot_offset);
896 object.member(index).set_obj(value_slot.value_unchecked());
897 }
898 }
899 _ => {
900 let class_name = class.name();
901 return crate::error!(
902 self,
903 "wrong number of arguments for constructing a '%s'",
904 class_name.as_bytes()
905 )
906 .map_err(Into::into);
907 }
908 }
909
910 self.set_stack_top(self.restore_stack(value_slot_offset));
911
912 if object_gco.is_black() {
913 self.barrier_back(object_gco, object.gc_list_slot());
914 }
915
916 Ok(1)
917 }
918 }
919}