1use crate::ObjectMemberLookup;
2use crate::call::{CallRuntime, ThreadStack};
3use crate::class::ClassRuntime;
4use crate::debug::DebugRuntime;
5use crate::function::FunctionRuntime;
6use crate::function::{Closure, Proto};
7use crate::gc::GcObject;
8use crate::gc::{GcBarrier, GcRuntime};
9use crate::handle::RawHandle;
10use crate::handle::sealed::Sealed;
11use crate::metamethod::{MetamethodRuntime, TmEvent};
12use crate::native::NativeCallContext;
13use crate::state::ThreadState;
14use crate::state::{LUA_CALLINFO_NATIVE, LUA_CALLINFO_OP_YIELD};
15use crate::table::TableRuntime;
16use crate::thread::{LUA_MULTRET, Thread};
17use crate::types::{
18 LUA_TBOOLEAN, LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TINTEGER, LUA_TLIGHTUSERDATA,
19 LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT, LUA_TSTRING, LUA_TTABLE, LUA_TTHREAD, LUA_TUSERDATA,
20 LUA_TVECTOR, LUA_VECTOR_SIZE,
21};
22use crate::userdata::{LuaUserdataDirectFieldGet, USERDATA_TAG_LIMIT, UserdataDirectFieldResult};
23use crate::value::{RAW_TVALUE_NIL, RawTValue, TValue, TValueCursor, nil_object};
24use crate::vm::{VmCallFrame, VmOperations, string_compare};
25use crate::{VmExit, VmResult};
26use luau_bytecode::model::{Instruction, InstructionAux};
27use luau_bytecode::opcodes::Opcode;
28use luau_common::flags;
29use std::mem;
30use std::ptr;
31
32const LCT_VAL: u8 = 0;
33const LCT_REF: u8 = 1;
34const LCT_UPVAL: u8 = 2;
35
36#[allow(
44 clippy::missing_safety_doc,
45 reason = "all methods share the capability-level safety contract"
46)]
47pub trait VmExecution: Sealed {
48 unsafe fn execute(&self) -> VmResult;
50
51 unsafe fn finish_op(&self) -> VmResult;
53}
54
55trait InstructionWordExt {
56 fn word(self) -> u32;
57 fn opcode(self) -> Opcode;
58 fn a(self) -> u8;
59 fn b(self) -> u8;
60 fn c(self) -> u8;
61 fn d(self) -> i16;
62 fn e(self) -> i32;
63}
64
65impl InstructionWordExt for u32 {
66 fn word(self) -> u32 {
67 self
68 }
69
70 fn opcode(self) -> Opcode {
71 decode_instruction_opcode(Instruction::new(self))
72 }
73
74 fn a(self) -> u8 {
75 Instruction::new(self).a()
76 }
77
78 fn b(self) -> u8 {
79 Instruction::new(self).b()
80 }
81
82 fn c(self) -> u8 {
83 Instruction::new(self).c()
84 }
85
86 fn d(self) -> i16 {
87 Instruction::new(self).d()
88 }
89
90 fn e(self) -> i32 {
91 Instruction::new(self).e()
92 }
93}
94
95trait InstructionBits {
96 fn bits(self) -> u32;
97}
98
99impl InstructionBits for u32 {
100 fn bits(self) -> u32 {
101 self
102 }
103}
104
105impl InstructionBits for Instruction {
106 fn bits(self) -> u32 {
107 self.word()
108 }
109}
110
111#[inline(always)]
112fn v_skip_step(op: Opcode) -> bool {
113 matches!(op, Opcode::PrepVarargs | Opcode::Break)
114}
115
116impl Proto {
117 #[inline(always)]
119 fn vm_const(&self, constants: TValueCursor, index: usize) -> TValue {
120 debug_assert!(index < unsafe { self.as_ptr().as_ref().unwrap_unchecked().size_k as usize });
121 unsafe { constants.add(index).value_unchecked() }
122 }
123
124 fn assert_vm_pc(&self, pc: *const u32) {
126 let code = unsafe { self.as_ptr().as_ref().unwrap_unchecked().code }.cast_const();
127 let size = unsafe { self.as_ptr().as_ref().unwrap_unchecked().size_code as usize };
128 debug_assert!(pc >= code);
129 debug_assert!(unsafe { pc.offset_from(code) as usize } < size);
130 }
131}
132
133impl TValueCursor {
134 fn vm_reg_cursor(self, top: TValueCursor, index: usize) -> TValueCursor {
135 debug_assert!(index < unsafe { top.offset_from(self) as usize });
136 unsafe { self.add(index) }
137 }
138}
139
140impl TValue {
141 #[inline(always)]
143 unsafe fn vector_ptr(&self) -> *const f32 {
144 debug_assert_eq!(self.tt(), LUA_TVECTOR);
145 unsafe { (&raw const self.as_ptr().as_ref().unwrap_unchecked().value.vector).cast::<f32>() }
146 }
147
148 #[inline(always)]
149 unsafe fn vector_eq(&self, rhs: TValue) -> bool {
150 unsafe {
151 let lhs = self.vector_ptr();
152 let rhs = rhs.vector_ptr();
153
154 #[cfg(feature = "vector4")]
155 {
156 *lhs == *rhs
157 && *lhs.add(1) == *rhs.add(1)
158 && *lhs.add(2) == *rhs.add(2)
159 && vector_w_ptr(lhs) == vector_w_ptr(rhs)
160 }
161
162 #[cfg(not(feature = "vector4"))]
163 {
164 *lhs == *rhs && *lhs.add(1) == *rhs.add(1) && *lhs.add(2) == *rhs.add(2)
165 }
166 }
167 }
168
169 #[inline(always)]
171 fn set_vector4(&self, x: f32, y: f32, z: f32, w: f32) {
172 #[cfg(not(feature = "vector4"))]
173 {
174 let _ = w;
175 self.set_vector([x, y, z]);
176 }
177
178 #[cfg(feature = "vector4")]
179 self.set_vector([x, y, z, w]);
180 }
181}
182
183#[cfg(feature = "vector4")]
184#[inline(always)]
185unsafe fn vector_w_ptr(value: *const f32) -> f32 {
186 unsafe { *value.add(3) }
187}
188
189#[cfg(not(feature = "vector4"))]
190#[inline(always)]
191unsafe fn vector_w_ptr(_value: *const f32) -> f32 {
192 0.0
193}
194
195impl Thread {
196 #[inline(always)]
198 fn set_vm_saved_pc<T>(&self, pc: *const T) {
199 unsafe {
200 self.current_call_info().set_saved_pc(pc.cast());
201 }
202 }
203
204 #[inline(always)]
206 fn interrupt_vm(&self, pc: *const u32) -> VmResult {
207 let global = unsafe { self.global() };
208 let Some(interrupt) = global.take_execution_interrupt_callback() else {
209 return Ok(());
210 };
211
212 unsafe { self.interrupt_vm_slow(pc, interrupt) }
213 }
214
215 #[cold]
216 #[inline(never)]
217 unsafe fn interrupt_vm_slow(
218 &self,
219 pc: *const u32,
220 interrupt: fn(&Thread) -> VmResult,
221 ) -> VmResult {
222 unsafe {
223 self.current_call_info().set_saved_pc(pc.add(1));
224 let result = interrupt(self);
225 if let Err(exit @ VmExit::Control(_)) = result {
226 let current_call_info = self.current_call_info();
227 current_call_info.set_saved_pc(current_call_info.saved_pc().sub(1));
228 return Err(exit);
229 }
230
231 result
232 }
233 }
234
235 unsafe fn tm_by_obj_or_nil(&self, object: TValue, event: TmEvent) -> TValue {
236 unsafe { self.get_tm_by_obj(object, event).unwrap_or_else(nil_object) }
237 }
238
239 #[cold]
240 #[inline(never)]
241 unsafe fn finish_native_call(&self, called_closure: Closure, nresults: i32) -> VmResult {
242 unsafe {
243 let native_function = called_closure.native_data().function.unwrap_unchecked();
244 let produced = native_function(NativeCallContext::new(self))?;
245
246 let call_info = self.current_call_info();
247 let parent_cursor = self.current_call_info_cursor().sub(1);
248 let parent = parent_cursor.call_info_unchecked();
249
250 let mut result = call_info.function();
251 let mut value = self.stack_top().sub(produced);
252 let value_end = self.stack_top();
253 let mut remaining = nresults;
254
255 while remaining != 0 && value < value_end {
256 result.value_unchecked().set_obj(value.value_unchecked());
257 result = result.add(1);
258 value = value.add(1);
259 remaining -= 1;
260 }
261
262 while remaining > 0 {
263 result.value_unchecked().set_nil();
264 result = result.add(1);
265 remaining -= 1;
266 }
267
268 self.set_current_call_info(parent_cursor);
269 self.set_stack_base(parent.base());
270 self.set_stack_top(if nresults == LUA_MULTRET {
271 result
272 } else {
273 parent.top()
274 });
275 Ok(())
276 }
277 }
278
279 pub(crate) unsafe fn execute_thread(&self) -> VmResult {
280 if unsafe { self.as_ptr().as_ref().unwrap_unchecked().single_step } {
281 v_execute_impl::<true>(self)
282 } else {
283 v_execute_impl::<false>(self)
284 }
285 }
286
287 pub(crate) unsafe fn finish_op_thread(&self) -> VmResult {
288 unsafe {
289 let call_info = self.current_call_info();
290 call_info.as_ptr().as_mut().unwrap_unchecked().flags &= !LUA_CALLINFO_OP_YIELD;
291
292 let closure = call_info.function_closure();
293 let base = self.stack_base();
294 let mut pc = call_info.saved_pc();
295 let instruction = *pc.sub(1);
296
297 match decode_instruction_opcode(instruction) {
298 Opcode::ForGLoop => {
299 let ra = base.vm_reg_cursor(self.stack_top(), instruction.a() as usize);
300
301 ra.add(2)
302 .value_unchecked()
303 .set_obj(ra.add(3).value_unchecked());
304
305 pc = if ra.add(3).value_unchecked().is_nil() {
306 pc.add(1)
307 } else {
308 pc.offset(instruction.d() as isize)
309 };
310
311 closure.proto().unwrap_unchecked().assert_vm_pc(pc);
312 }
313 _ => unreachable!("unknown yielded opcode"),
314 }
315
316 call_info.set_saved_pc(pc);
317 }
318 Ok(())
319 }
320
321 unsafe fn setup_cci(&self, n_results: i32, function: TValueCursor) -> VmResult {
322 unsafe {
323 let call_info = self.incr_ci()?.call_info_unchecked();
324 let closure = function.value_unchecked().closure_value();
325
326 call_info.init_call(
327 function,
328 function.add(1).add(crate::thread::LUA_MIN_STACK),
329 n_results,
330 closure.proto(),
331 );
332
333 self.set_stack_base(function.add(1));
334 self.check_stack_for_new_ci(crate::thread::LUA_MIN_STACK as i32)?;
335
336 debug_assert!(call_info.top() <= self.stack_last());
337 debug_assert!(call_info.function().value_unchecked().is_function());
338 }
339 Ok(())
340 }
341}
342
343impl VmExecution for Thread {
344 unsafe fn execute(&self) -> VmResult {
346 unsafe { self.execute_thread() }
347 }
348
349 unsafe fn finish_op(&self) -> VmResult {
351 unsafe { self.finish_op_thread() }
352 }
353}
354
355fn patch_instruction_c<T>(instruction: *mut T, c: u8) {
356 unsafe {
357 let instruction = instruction.cast::<Instruction>();
358 *instruction = Instruction::new((*instruction).word()).with_c(c);
359 }
360}
361
362fn patch_instruction_opcode<T>(instruction: *mut T, opcode: Opcode) {
363 unsafe {
364 let instruction = instruction.cast::<Instruction>();
365 *instruction = Instruction::new(((*instruction).word() & !0xff) | opcode as u32);
366 }
367}
368
369fn patch_aux_slot<T>(aux: *mut T, kv: u16, slot: u16) {
370 unsafe {
371 let aux = aux.cast::<Instruction>();
372 *aux = Instruction::new(kv as u32 | ((slot as u32) << 16));
373 }
374}
375
376#[inline(always)]
377fn decode_opcode_byte(opcode: u8) -> Opcode {
378 debug_assert!(usize::from(opcode) < Opcode::COUNT);
379 unsafe { mem::transmute(opcode) }
380}
381
382#[inline(always)]
383fn decode_instruction_opcode<T: InstructionBits>(instruction: T) -> Opcode {
384 decode_opcode_byte((instruction.bits() & 0xff) as u8)
385}
386
387fn v_execute_impl<const SINGLE_STEP: bool>(thread: &Thread) -> VmResult {
388 unsafe {
389 let global = thread.global();
390 let current_call_info = thread.current_call_info();
391
392 debug_assert!(current_call_info.is_lua());
393 debug_assert!(thread.as_ptr().as_ref().unwrap_unchecked().is_active);
394 debug_assert!(!GcObject::from(thread).is_black());
395
396 let mut pc = current_call_info.saved_pc();
397 let mut closure = current_call_info.function_closure();
398 let mut base = thread.stack_base();
399 let mut proto = closure.proto().unwrap_unchecked();
400 let mut constants = proto.constants();
401
402 if current_call_info.as_ptr().as_ref().unwrap_unchecked().flags & LUA_CALLINFO_NATIVE != 0
403 && !SINGLE_STEP
404 {
405 debug_assert!(
406 !proto
407 .as_ptr()
408 .as_ref()
409 .unwrap_unchecked()
410 .exec_data
411 .is_null()
412 );
413
414 let Some(enter) = global.execution_enter() else {
415 unreachable!("native lvmexecute requires an execution enter callback");
416 };
417
418 if enter(thread, proto) == 0 {
419 return Ok(());
420 }
421
422 let resumed_call_info = thread.current_call_info();
423 closure = resumed_call_info.function_closure();
424 proto = closure.proto().unwrap_unchecked();
425 pc = resumed_call_info.saved_pc();
426 base = thread.stack_base();
427 constants = proto.constants();
428 proto.assert_vm_pc(pc);
429 }
430
431 'vm: loop {
432 debug_assert!(base == thread.stack_base());
433 debug_assert!(base <= thread.stack_top());
434 debug_assert!(thread.stack_top() <= thread.stack_last());
435
436 let insn_pc = pc;
437 let instruction = { *insn_pc };
438 let opcode = decode_instruction_opcode(instruction);
439 if SINGLE_STEP
440 && let Some(hook) = global.debug_step_callback()
441 && !v_skip_step(opcode)
442 {
443 thread.set_vm_saved_pc(insn_pc);
444 thread.call_hook(hook, ptr::null_mut())?;
445
446 base = thread.stack_base();
447 }
448
449 pc = insn_pc.add(1);
450
451 let mut dispatch_opcode = opcode;
452
453 'dispatch: loop {
454 match dispatch_opcode {
455 Opcode::Nop => {
456 debug_assert_eq!(instruction.word(), 0);
457 }
458 Opcode::Break => {
459 let debug_insn = { proto.as_ptr().as_ref().unwrap_unchecked().debug_insn };
460 debug_assert!(!debug_insn.is_null());
461
462 let debug_pc = {
463 insn_pc.offset_from(proto.as_ptr().as_ref().unwrap_unchecked().code)
464 as usize
465 };
466 let original_opcode = { *debug_insn.add(debug_pc) };
467 debug_assert_ne!(original_opcode, Opcode::Break as u8);
468
469 if let Some(hook) = global.debug_break_callback() {
470 thread.set_vm_saved_pc(insn_pc);
471 thread.call_hook(hook, ptr::null_mut())?;
472
473 base = thread.stack_base();
474 }
475
476 dispatch_opcode = decode_opcode_byte(original_opcode);
477 continue 'dispatch;
478 }
479 Opcode::LoadNil => {
480 let ra = base
481 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
482 .value_unchecked();
483 ra.set_nil();
484 }
485 Opcode::LoadB => {
486 let ra = base
487 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
488 .value_unchecked();
489 ra.set_boolean(i32::from(instruction.b()));
490
491 pc = pc.add(instruction.c() as usize);
492 proto.assert_vm_pc(pc);
493 }
494 Opcode::LoadN => {
495 let ra = base
496 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
497 .value_unchecked();
498 ra.set_number(f64::from(instruction.d()));
499 }
500 Opcode::LoadK => {
501 let ra = base
502 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
503 .value_unchecked();
504 let kv = proto.vm_const(constants, instruction.d() as u16 as usize);
505 ra.set_obj(kv);
506 }
507 Opcode::Move => {
508 let top = thread.stack_top();
509 let ra = base
510 .vm_reg_cursor(top, instruction.a() as usize)
511 .value_unchecked();
512 let rb = base
513 .vm_reg_cursor(top, instruction.b() as usize)
514 .value_unchecked();
515 ra.set_obj(rb);
516 }
517 Opcode::GetGlobal => {
518 let ra_cursor =
519 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
520 let ra = ra_cursor.value_unchecked();
521 let aux = { InstructionAux::new((*pc).word()) };
522 pc = pc.add(1);
523 let kv = proto.vm_const(constants, aux.word() as usize);
524 debug_assert!(kv.is_string());
525
526 let env = closure.env();
527 let slot = instruction.c() as usize & env.node_mask_8();
528 let node = env.node(slot as i32);
529
530 if node.has_string_key(kv.string_value()) && !node.value_is_nil() {
531 ra.set_obj(node.value_unchecked());
532 } else {
533 let mut global_storage = RAW_TVALUE_NIL;
534 let global = TValue::from_mut(&mut global_storage);
535 global.set_table_value(env);
536 thread.set_cached_slot(slot as i32);
537 thread.set_vm_saved_pc(pc);
538 thread.get_table_internal(global, kv, ra_cursor)?;
539 let cached_slot = thread.cached_slot();
540 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
541 base = thread.stack_base();
542 }
543 }
544 Opcode::SetGlobal => {
545 let ra = base
546 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
547 .value_unchecked();
548 let aux = { InstructionAux::new((*pc).word()) };
549 pc = pc.add(1);
550 let kv = proto.vm_const(constants, aux.word() as usize);
551 debug_assert!(kv.is_string());
552
553 let env = closure.env();
554 let slot = instruction.c() as usize & env.node_mask_8();
555 let node = env.node(slot as i32);
556
557 if node.has_string_key(kv.string_value()) && !node.value_is_nil() && {
558 env.as_ptr().as_ref().unwrap_unchecked().readonly == 0
559 } {
560 node.value_unchecked().set_obj(ra);
561 if ra.is_collectable() {
562 let table_object: GcObject = env.into();
563 let child = ra.gc_value();
564 if table_object.is_black() && child.is_white() {
565 thread.barrier_table(env, child);
566 }
567 }
568 } else {
569 let mut global_storage = RAW_TVALUE_NIL;
570 let global = TValue::from_mut(&mut global_storage);
571 global.set_table_value(env);
572 thread.set_cached_slot(slot as i32);
573 thread.set_vm_saved_pc(pc);
574 thread.set_table_internal(global, kv, ra)?;
575 let cached_slot = thread.cached_slot();
576 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
577 base = thread.stack_base();
578 }
579 }
580 Opcode::NewClosure => {
581 let ra = base
582 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
583 .value_unchecked();
584 let child_proto_index = instruction.d() as u16 as usize;
585 debug_assert!(
586 child_proto_index
587 < proto.as_ptr().as_ref().unwrap_unchecked().size_p as usize
588 );
589 let child_proto = proto.child_proto(child_proto_index).unwrap_unchecked();
590
591 thread.set_vm_saved_pc(pc);
592
593 let env = Some(closure.env());
594 let new_closure = thread.new_lua_closure(
595 i32::from(child_proto.as_ptr().as_ref().unwrap_unchecked().n_ups),
596 env,
597 child_proto,
598 )?;
599 ra.set_closure_value(new_closure);
600
601 for upvalue_index in
602 0..child_proto.as_ptr().as_ref().unwrap_unchecked().n_ups as usize
603 {
604 let capture_instruction = { *pc };
605 pc = pc.add(1);
606 debug_assert_eq!(capture_instruction.opcode(), Opcode::Capture);
607
608 match capture_instruction.a() {
609 LCT_VAL => {
610 let captured = base
611 .vm_reg_cursor(
612 thread.stack_top(),
613 capture_instruction.b() as usize,
614 )
615 .value_unchecked();
616 new_closure.lua_upvalue_ref(upvalue_index).set_obj(captured);
617 }
618 LCT_REF => {
619 let captured = base
620 .vm_reg_cursor(
621 thread.stack_top(),
622 capture_instruction.b() as usize,
623 )
624 .value_unchecked();
625 let upvalue = thread.find_upvalue(captured)?;
626 new_closure
627 .lua_upvalue_ref(upvalue_index)
628 .set_upvalue_value(upvalue);
629 }
630 LCT_UPVAL => {
631 let captured =
632 closure.lua_upvalue_ref(capture_instruction.b() as usize);
633 new_closure.lua_upvalue_ref(upvalue_index).set_obj(captured);
634 }
635 _ => unreachable!("unknown upvalue capture type"),
636 }
637 }
638
639 thread.set_vm_saved_pc(pc);
640 thread.check_gc()?;
641 base = thread.stack_base();
642 }
643 Opcode::DupClosure => {
644 let ra = base
645 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
646 .value_unchecked();
647 let kv = proto.vm_const(constants, instruction.d() as u16 as usize);
648 let constant_closure = { kv.closure_value() };
649
650 thread.set_vm_saved_pc(pc);
651
652 let env = closure.env();
653 let mut new_closure = if constant_closure.env() == env {
654 constant_closure
655 } else {
656 let constant_proto = { constant_closure.proto().unwrap_unchecked() };
657 thread.new_lua_closure(
658 i32::from(
659 constant_closure
660 .as_ptr()
661 .as_ref()
662 .unwrap_unchecked()
663 .n_upvalues,
664 ),
665 Some(env),
666 constant_proto,
667 )?
668 };
669 ra.set_closure_value(new_closure);
670
671 let upvalue_count = {
672 constant_closure
673 .as_ptr()
674 .as_ref()
675 .unwrap_unchecked()
676 .n_upvalues as usize
677 };
678 let mut upvalue_index = 0usize;
679 while upvalue_index < upvalue_count {
680 let capture_instruction = { *pc.add(upvalue_index) };
681 debug_assert_eq!(capture_instruction.opcode(), Opcode::Capture);
682
683 debug_assert!(matches!(capture_instruction.a(), LCT_VAL | LCT_UPVAL));
684
685 let captured = match capture_instruction.a() {
686 LCT_VAL => base
687 .vm_reg_cursor(
688 thread.stack_top(),
689 capture_instruction.b() as usize,
690 )
691 .value_unchecked(),
692 LCT_UPVAL => {
693 closure.lua_upvalue_ref(capture_instruction.b() as usize)
694 }
695 _ => unreachable!("DUPCLOSURE capture stream must not contain REF"),
696 };
697
698 let target = new_closure.lua_upvalue_ref(upvalue_index);
699
700 if new_closure == constant_closure && target.raw_equal(captured) {
701 upvalue_index += 1;
702 continue;
703 }
704
705 if new_closure == constant_closure
706 && constant_closure
707 .as_ptr()
708 .as_ref()
709 .unwrap_unchecked()
710 .preload
711 == 0
712 {
713 let constant_proto = constant_closure.proto().unwrap_unchecked();
714 new_closure = thread.new_lua_closure(
715 i32::from(
716 constant_closure
717 .as_ptr()
718 .as_ref()
719 .unwrap_unchecked()
720 .n_upvalues,
721 ),
722 Some(env),
723 constant_proto,
724 )?;
725 {
726 ra.set_closure_value(new_closure);
727 }
728
729 upvalue_index = 0;
730 continue;
731 }
732
733 target.set_obj(captured);
734
735 if captured.is_collectable() {
736 let object: GcObject = new_closure.into();
737 let child = captured.gc_value();
738 if object.is_black() && child.is_white() {
739 thread.barrier_forward(object, child);
740 }
741 }
742
743 upvalue_index += 1;
744 }
745
746 {
747 new_closure.as_ptr().as_mut().unwrap_unchecked().preload = 0;
748 }
749
750 if constant_closure != new_closure {
751 thread.set_vm_saved_pc(pc);
752 thread.check_gc()?;
753 base = thread.stack_base();
754 }
755 pc = pc.add(upvalue_count);
756 }
757 Opcode::And => {
758 let ra = base
759 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
760 .value_unchecked();
761 let rb = base
762 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
763 .value_unchecked();
764 let rc = base
765 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
766 .value_unchecked();
767 let value = if rb.is_false() { rb } else { rc };
768
769 ra.set_obj(value);
770 }
771 Opcode::Or => {
772 let ra = base
773 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
774 .value_unchecked();
775 let rb = base
776 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
777 .value_unchecked();
778 let rc = base
779 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
780 .value_unchecked();
781 let value = if rb.is_false() { rc } else { rb };
782
783 ra.set_obj(value);
784 }
785 Opcode::AndK => {
786 let ra = base
787 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
788 .value_unchecked();
789 let rb = base
790 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
791 .value_unchecked();
792 let kv = proto.vm_const(constants, instruction.c() as usize);
793 let value = if rb.is_false() { rb } else { kv };
794
795 ra.set_obj(value);
796 }
797 Opcode::OrK => {
798 let ra = base
799 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
800 .value_unchecked();
801 let rb = base
802 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
803 .value_unchecked();
804 let kv = proto.vm_const(constants, instruction.c() as usize);
805 let value = if rb.is_false() { kv } else { rb };
806
807 ra.set_obj(value);
808 }
809 Opcode::Not => {
810 let ra = base
811 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
812 .value_unchecked();
813 let rb = base
814 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
815 .value_unchecked();
816
817 {
818 ra.set_boolean(i32::from(rb.is_false()));
819 }
820 }
821 Opcode::Add => {
822 let ra_cursor =
823 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
824 let ra = ra_cursor.value_unchecked();
825 let rb = base
826 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
827 .value_unchecked();
828 let rc = base
829 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
830 .value_unchecked();
831
832 if rb.is_number() && rc.is_number() {
833 {
834 ra.set_number(rb.number_value() + rc.number_value());
835 }
836 } else if rb.is_vector() && rc.is_vector() {
837 let bx = { rb.vector_ptr() };
838 let cx = { rc.vector_ptr() };
839 {
840 ra.set_vector4(
841 *bx + *cx,
842 *bx.add(1) + *cx.add(1),
843 *bx.add(2) + *cx.add(2),
844 vector_w_ptr(bx) + vector_w_ptr(cx),
845 );
846 }
847 } else {
848 let function = thread.tm_by_obj_or_nil(rb, TmEvent::Add);
849 if { rb.is_userdata() } && {
850 function.is_function() && function.is_native_function()
851 } {
852 let top_cursor = thread.stack_top();
853 debug_assert!({
854 top_cursor.add(3)
855 < thread.stack().add(
856 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
857 as usize,
858 )
859 });
860 {
861 top_cursor.value_unchecked().set_obj(function);
862 top_cursor.add(1).value_unchecked().set_obj(rb);
863 top_cursor.add(2).value_unchecked().set_obj(rc);
864 thread.set_stack_top(top_cursor.add(3));
865 }
866
867 thread.set_vm_saved_pc(pc);
868 thread.call_tm(2, i32::from(instruction.a()))?;
869 base = thread.stack_base();
870 } else {
871 thread.set_vm_saved_pc(pc);
872 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::Add)?;
873 base = thread.stack_base();
874 }
875 }
876 }
877 Opcode::Sub => {
878 let ra_cursor =
879 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
880 let ra = ra_cursor.value_unchecked();
881 let rb = base
882 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
883 .value_unchecked();
884 let rc = base
885 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
886 .value_unchecked();
887
888 if rb.is_number() && rc.is_number() {
889 {
890 ra.set_number(rb.number_value() - rc.number_value());
891 }
892 } else if rb.is_vector() && rc.is_vector() {
893 let bx = { rb.vector_ptr() };
894 let cx = { rc.vector_ptr() };
895 {
896 ra.set_vector4(
897 *bx - *cx,
898 *bx.add(1) - *cx.add(1),
899 *bx.add(2) - *cx.add(2),
900 vector_w_ptr(bx) - vector_w_ptr(cx),
901 );
902 }
903 } else {
904 let function = thread.tm_by_obj_or_nil(rb, TmEvent::Sub);
905 if { rb.is_userdata() } && {
906 function.is_function() && function.is_native_function()
907 } {
908 let top_cursor = thread.stack_top();
909 debug_assert!({
910 top_cursor.add(3)
911 < thread.stack().add(
912 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
913 as usize,
914 )
915 });
916 {
917 top_cursor.value_unchecked().set_obj(function);
918 top_cursor.add(1).value_unchecked().set_obj(rb);
919 top_cursor.add(2).value_unchecked().set_obj(rc);
920 thread.set_stack_top(top_cursor.add(3));
921 }
922
923 thread.set_vm_saved_pc(pc);
924 thread.call_tm(2, i32::from(instruction.a()))?;
925 base = thread.stack_base();
926 } else {
927 thread.set_vm_saved_pc(pc);
928 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::Sub)?;
929 base = thread.stack_base();
930 }
931 }
932 }
933 Opcode::Mul => {
934 let ra_cursor =
935 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
936 let ra = ra_cursor.value_unchecked();
937 let rb = base
938 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
939 .value_unchecked();
940 let rc = base
941 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
942 .value_unchecked();
943
944 if rb.is_number() && rc.is_number() {
945 {
946 ra.set_number(rb.number_value() * rc.number_value());
947 }
948 } else if rb.is_vector() && rc.is_number() {
949 let right = { rc.number_value() as f32 };
950 let bx = { rb.vector_ptr() };
951 {
952 ra.set_vector4(
953 *bx * right,
954 *bx.add(1) * right,
955 *bx.add(2) * right,
956 vector_w_ptr(bx) * right,
957 );
958 }
959 } else if rb.is_vector() && rc.is_vector() {
960 let bx = { rb.vector_ptr() };
961 let cx = { rc.vector_ptr() };
962 {
963 ra.set_vector4(
964 *bx * *cx,
965 *bx.add(1) * *cx.add(1),
966 *bx.add(2) * *cx.add(2),
967 vector_w_ptr(bx) * vector_w_ptr(cx),
968 );
969 }
970 } else if rb.is_number() && rc.is_vector() {
971 let left = { rb.number_value() as f32 };
972 let cx = { rc.vector_ptr() };
973 {
974 ra.set_vector4(
975 left * *cx,
976 left * *cx.add(1),
977 left * *cx.add(2),
978 left * vector_w_ptr(cx),
979 );
980 }
981 } else {
982 let tm_operand = if rb.is_number() { rc } else { rb };
983 let function = thread.tm_by_obj_or_nil(tm_operand, TmEvent::Mul);
984 if { tm_operand.is_userdata() } && {
985 function.is_function() && function.is_native_function()
986 } {
987 let top_cursor = thread.stack_top();
988 debug_assert!({
989 top_cursor.add(3)
990 < thread.stack().add(
991 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
992 as usize,
993 )
994 });
995 {
996 top_cursor.value_unchecked().set_obj(function);
997 top_cursor.add(1).value_unchecked().set_obj(rb);
998 top_cursor.add(2).value_unchecked().set_obj(rc);
999 thread.set_stack_top(top_cursor.add(3));
1000 }
1001
1002 thread.set_vm_saved_pc(pc);
1003 thread.call_tm(2, i32::from(instruction.a()))?;
1004 base = thread.stack_base();
1005 } else {
1006 thread.set_vm_saved_pc(pc);
1007 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::Mul)?;
1008 base = thread.stack_base();
1009 }
1010 }
1011 }
1012 Opcode::Div => {
1013 let ra_cursor =
1014 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1015 let ra = ra_cursor.value_unchecked();
1016 let rb = base
1017 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1018 .value_unchecked();
1019 let rc = base
1020 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
1021 .value_unchecked();
1022
1023 if rb.is_number() && rc.is_number() {
1024 {
1025 ra.set_number(rb.number_value() / rc.number_value());
1026 }
1027 } else if rb.is_vector() && rc.is_number() {
1028 let right = { rc.number_value() as f32 };
1029 let bx = { rb.vector_ptr() };
1030 {
1031 ra.set_vector4(
1032 *bx / right,
1033 *bx.add(1) / right,
1034 *bx.add(2) / right,
1035 vector_w_ptr(bx) / right,
1036 );
1037 }
1038 } else if rb.is_vector() && rc.is_vector() {
1039 let bx = { rb.vector_ptr() };
1040 let cx = { rc.vector_ptr() };
1041 {
1042 ra.set_vector4(
1043 *bx / *cx,
1044 *bx.add(1) / *cx.add(1),
1045 *bx.add(2) / *cx.add(2),
1046 vector_w_ptr(bx) / vector_w_ptr(cx),
1047 );
1048 }
1049 } else if rb.is_number() && rc.is_vector() {
1050 let left = { rb.number_value() as f32 };
1051 let cx = { rc.vector_ptr() };
1052 {
1053 ra.set_vector4(
1054 left / *cx,
1055 left / *cx.add(1),
1056 left / *cx.add(2),
1057 left / vector_w_ptr(cx),
1058 );
1059 }
1060 } else {
1061 let tm_operand = if rb.is_number() { rc } else { rb };
1062 let function = thread.tm_by_obj_or_nil(tm_operand, TmEvent::Div);
1063 if { tm_operand.is_userdata() } && {
1064 function.is_function() && function.is_native_function()
1065 } {
1066 let top_cursor = thread.stack_top();
1067 debug_assert!({
1068 top_cursor.add(3)
1069 < thread.stack().add(
1070 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1071 as usize,
1072 )
1073 });
1074 {
1075 top_cursor.value_unchecked().set_obj(function);
1076 top_cursor.add(1).value_unchecked().set_obj(rb);
1077 top_cursor.add(2).value_unchecked().set_obj(rc);
1078 thread.set_stack_top(top_cursor.add(3));
1079 }
1080
1081 thread.set_vm_saved_pc(pc);
1082 thread.call_tm(2, i32::from(instruction.a()))?;
1083 base = thread.stack_base();
1084 } else {
1085 thread.set_vm_saved_pc(pc);
1086 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::Div)?;
1087 base = thread.stack_base();
1088 }
1089 }
1090 }
1091 Opcode::IDiv => {
1092 let ra_cursor =
1093 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1094 let ra = ra_cursor.value_unchecked();
1095 let rb = base
1096 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1097 .value_unchecked();
1098 let rc = base
1099 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
1100 .value_unchecked();
1101
1102 if rb.is_number() && rc.is_number() {
1103 {
1104 ra.set_number(crate::number::num_idiv(
1105 rb.number_value(),
1106 rc.number_value(),
1107 ));
1108 }
1109 } else if rb.is_vector() && rc.is_number() {
1110 let right = { rc.number_value() as f32 };
1111 let bx = { rb.vector_ptr() };
1112 {
1113 ra.set_vector4(
1114 crate::number::num_idiv((*bx) as f64, right as f64) as f32,
1115 crate::number::num_idiv((*bx.add(1)) as f64, right as f64)
1116 as f32,
1117 crate::number::num_idiv((*bx.add(2)) as f64, right as f64)
1118 as f32,
1119 crate::number::num_idiv(vector_w_ptr(bx) as f64, right as f64)
1120 as f32,
1121 );
1122 }
1123 } else {
1124 let tm_operand = if rb.is_number() { rc } else { rb };
1125 let function = thread.tm_by_obj_or_nil(tm_operand, TmEvent::IDiv);
1126 if { tm_operand.is_userdata() } && {
1127 function.is_function() && function.is_native_function()
1128 } {
1129 let top_cursor = thread.stack_top();
1130 debug_assert!({
1131 top_cursor.add(3)
1132 < thread.stack().add(
1133 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1134 as usize,
1135 )
1136 });
1137 {
1138 top_cursor.value_unchecked().set_obj(function);
1139 top_cursor.add(1).value_unchecked().set_obj(rb);
1140 top_cursor.add(2).value_unchecked().set_obj(rc);
1141 thread.set_stack_top(top_cursor.add(3));
1142 }
1143
1144 thread.set_vm_saved_pc(pc);
1145 thread.call_tm(2, i32::from(instruction.a()))?;
1146 base = thread.stack_base();
1147 } else {
1148 thread.set_vm_saved_pc(pc);
1149 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::IDiv)?;
1150 base = thread.stack_base();
1151 }
1152 }
1153 }
1154 Opcode::Mod => {
1155 let ra_cursor =
1156 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1157 let ra = ra_cursor.value_unchecked();
1158 let rb = base
1159 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1160 .value_unchecked();
1161 let rc = base
1162 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
1163 .value_unchecked();
1164
1165 if rb.is_number() && rc.is_number() {
1166 {
1167 ra.set_number(crate::number::num_mod(
1168 rb.number_value(),
1169 rc.number_value(),
1170 ));
1171 }
1172 } else {
1173 thread.set_vm_saved_pc(pc);
1174 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::Mod)?;
1175 base = thread.stack_base();
1176 }
1177 }
1178 Opcode::Pow => {
1179 let ra_cursor =
1180 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1181 let ra = ra_cursor.value_unchecked();
1182 let rb = base
1183 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1184 .value_unchecked();
1185 let rc = base
1186 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
1187 .value_unchecked();
1188
1189 if rb.is_number() && rc.is_number() {
1190 {
1191 ra.set_number(rb.number_value().powf(rc.number_value()));
1192 }
1193 } else {
1194 thread.set_vm_saved_pc(pc);
1195 thread.do_arith_impl(ra_cursor, rb, rc, TmEvent::Pow)?;
1196 base = thread.stack_base();
1197 }
1198 }
1199 Opcode::AddK => {
1200 let ra_cursor =
1201 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1202 let ra = ra_cursor.value_unchecked();
1203 let rb = base
1204 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1205 .value_unchecked();
1206 let kv = proto.vm_const(constants, instruction.c() as usize);
1207
1208 if rb.is_number() {
1209 {
1210 ra.set_number(rb.number_value() + kv.number_value());
1211 }
1212 } else {
1213 thread.set_vm_saved_pc(pc);
1214 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::Add)?;
1215 base = thread.stack_base();
1216 }
1217 }
1218 Opcode::SubK => {
1219 let ra_cursor =
1220 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1221 let ra = ra_cursor.value_unchecked();
1222 let rb = base
1223 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1224 .value_unchecked();
1225 let kv = proto.vm_const(constants, instruction.c() as usize);
1226
1227 if rb.is_number() {
1228 {
1229 ra.set_number(rb.number_value() - kv.number_value());
1230 }
1231 } else {
1232 thread.set_vm_saved_pc(pc);
1233 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::Sub)?;
1234 base = thread.stack_base();
1235 }
1236 }
1237 Opcode::MulK => {
1238 let ra_cursor =
1239 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1240 let ra = ra_cursor.value_unchecked();
1241 let rb = base
1242 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1243 .value_unchecked();
1244 let kv = proto.vm_const(constants, instruction.c() as usize);
1245
1246 if rb.is_number() {
1247 {
1248 ra.set_number(rb.number_value() * kv.number_value());
1249 }
1250 } else if rb.is_vector() {
1251 let right = { kv.number_value() as f32 };
1252 let bx = { rb.vector_ptr() };
1253 {
1254 ra.set_vector4(
1255 *bx * right,
1256 *bx.add(1) * right,
1257 *bx.add(2) * right,
1258 vector_w_ptr(bx) * right,
1259 );
1260 }
1261 } else {
1262 let function = thread.tm_by_obj_or_nil(rb, TmEvent::Mul);
1263 if { rb.is_userdata() } && {
1264 function.is_function() && function.is_native_function()
1265 } {
1266 let top_cursor = thread.stack_top();
1267 debug_assert!({
1268 top_cursor.add(3)
1269 < thread.stack().add(
1270 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1271 as usize,
1272 )
1273 });
1274 {
1275 top_cursor.value_unchecked().set_obj(function);
1276 top_cursor.add(1).value_unchecked().set_obj(rb);
1277 top_cursor.add(2).value_unchecked().set_obj(kv);
1278 thread.set_stack_top(top_cursor.add(3));
1279 }
1280
1281 thread.set_vm_saved_pc(pc);
1282 thread.call_tm(2, i32::from(instruction.a()))?;
1283 base = thread.stack_base();
1284 } else {
1285 thread.set_vm_saved_pc(pc);
1286 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::Mul)?;
1287 base = thread.stack_base();
1288 }
1289 }
1290 }
1291 Opcode::DivK => {
1292 let ra_cursor =
1293 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1294 let ra = ra_cursor.value_unchecked();
1295 let rb = base
1296 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1297 .value_unchecked();
1298 let kv = proto.vm_const(constants, instruction.c() as usize);
1299
1300 if rb.is_number() {
1301 {
1302 ra.set_number(rb.number_value() / kv.number_value());
1303 }
1304 } else if rb.is_vector() {
1305 let right = { kv.number_value() as f32 };
1306 let bx = { rb.vector_ptr() };
1307 {
1308 ra.set_vector4(
1309 *bx / right,
1310 *bx.add(1) / right,
1311 *bx.add(2) / right,
1312 vector_w_ptr(bx) / right,
1313 );
1314 }
1315 } else {
1316 let function = thread.tm_by_obj_or_nil(rb, TmEvent::Div);
1317 if { rb.is_userdata() } && {
1318 function.is_function() && function.is_native_function()
1319 } {
1320 let top_cursor = thread.stack_top();
1321 debug_assert!({
1322 top_cursor.add(3)
1323 < thread.stack().add(
1324 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1325 as usize,
1326 )
1327 });
1328 {
1329 top_cursor.value_unchecked().set_obj(function);
1330 top_cursor.add(1).value_unchecked().set_obj(rb);
1331 top_cursor.add(2).value_unchecked().set_obj(kv);
1332 thread.set_stack_top(top_cursor.add(3));
1333 }
1334
1335 thread.set_vm_saved_pc(pc);
1336 thread.call_tm(2, i32::from(instruction.a()))?;
1337 base = thread.stack_base();
1338 } else {
1339 thread.set_vm_saved_pc(pc);
1340 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::Div)?;
1341 base = thread.stack_base();
1342 }
1343 }
1344 }
1345 Opcode::IDivK => {
1346 let ra_cursor =
1347 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1348 let ra = ra_cursor.value_unchecked();
1349 let rb = base
1350 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1351 .value_unchecked();
1352 let kv = proto.vm_const(constants, instruction.c() as usize);
1353
1354 if rb.is_number() {
1355 {
1356 ra.set_number(crate::number::num_idiv(
1357 rb.number_value(),
1358 kv.number_value(),
1359 ));
1360 }
1361 } else if rb.is_vector() {
1362 let right = { kv.number_value() as f32 };
1363 let bx = { rb.vector_ptr() };
1364 {
1365 ra.set_vector4(
1366 crate::number::num_idiv((*bx) as f64, right as f64) as f32,
1367 crate::number::num_idiv((*bx.add(1)) as f64, right as f64)
1368 as f32,
1369 crate::number::num_idiv((*bx.add(2)) as f64, right as f64)
1370 as f32,
1371 crate::number::num_idiv(vector_w_ptr(bx) as f64, right as f64)
1372 as f32,
1373 );
1374 }
1375 } else {
1376 let function = thread.tm_by_obj_or_nil(rb, TmEvent::IDiv);
1377 if { rb.is_userdata() } && {
1378 function.is_function() && function.is_native_function()
1379 } {
1380 let top_cursor = thread.stack_top();
1381 debug_assert!({
1382 top_cursor.add(3)
1383 < thread.stack().add(
1384 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1385 as usize,
1386 )
1387 });
1388 {
1389 top_cursor.value_unchecked().set_obj(function);
1390 top_cursor.add(1).value_unchecked().set_obj(rb);
1391 top_cursor.add(2).value_unchecked().set_obj(kv);
1392 thread.set_stack_top(top_cursor.add(3));
1393 }
1394
1395 thread.set_vm_saved_pc(pc);
1396 thread.call_tm(2, i32::from(instruction.a()))?;
1397 base = thread.stack_base();
1398 } else {
1399 thread.set_vm_saved_pc(pc);
1400 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::IDiv)?;
1401 base = thread.stack_base();
1402 }
1403 }
1404 }
1405 Opcode::ModK => {
1406 let ra_cursor =
1407 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1408 let ra = ra_cursor.value_unchecked();
1409 let rb = base
1410 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1411 .value_unchecked();
1412 let kv = proto.vm_const(constants, instruction.c() as usize);
1413
1414 if rb.is_number() {
1415 {
1416 ra.set_number(crate::number::num_mod(
1417 rb.number_value(),
1418 kv.number_value(),
1419 ));
1420 }
1421 } else {
1422 thread.set_vm_saved_pc(pc);
1423 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::Mod)?;
1424 base = thread.stack_base();
1425 }
1426 }
1427 Opcode::PowK => {
1428 let ra_cursor =
1429 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1430 let ra = ra_cursor.value_unchecked();
1431 let rb = base
1432 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1433 .value_unchecked();
1434 let kv = proto.vm_const(constants, instruction.c() as usize);
1435
1436 if rb.is_number() {
1437 let left = { rb.number_value() };
1438 let right = { kv.number_value() };
1439 let result = if right == 2.0 {
1440 left * left
1441 } else if right == 0.5 {
1442 left.sqrt()
1443 } else if right == 3.0 {
1444 left * left * left
1445 } else {
1446 left.powf(right)
1447 };
1448
1449 {
1450 ra.set_number(result);
1451 }
1452 } else {
1453 thread.set_vm_saved_pc(pc);
1454 thread.do_arith_impl(ra_cursor, rb, kv, TmEvent::Pow)?;
1455 base = thread.stack_base();
1456 }
1457 }
1458 Opcode::Concat => {
1459 let b = instruction.b() as i32;
1460 let c = instruction.c() as i32;
1461
1462 thread.set_vm_saved_pc(pc);
1463 thread.concat_internal(c - b + 1, c)?;
1464 base = thread.stack_base();
1465
1466 let ra = base
1467 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1468 .value_unchecked();
1469 ra.set_obj(base.add(b as usize).value_unchecked());
1470
1471 thread.set_vm_saved_pc(pc);
1472 thread.check_gc()?;
1473 base = thread.stack_base();
1474 }
1475 Opcode::Minus => {
1476 let ra_cursor =
1477 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1478 let ra = ra_cursor.value_unchecked();
1479 let rb = base
1480 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1481 .value_unchecked();
1482
1483 if rb.is_number() {
1484 {
1485 ra.set_number(-rb.number_value());
1486 }
1487 } else if rb.is_vector() {
1488 let bx = { rb.vector_ptr() };
1489 {
1490 ra.set_vector4(-*bx, -*bx.add(1), -*bx.add(2), -vector_w_ptr(bx));
1491 }
1492 } else {
1493 let function = thread.tm_by_obj_or_nil(rb, TmEvent::Unm);
1494 if { rb.is_userdata() } && {
1495 function.is_function() && function.is_native_function()
1496 } {
1497 let top_cursor = thread.stack_top();
1498 debug_assert!({
1499 top_cursor.add(2)
1500 < thread.stack().add(
1501 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1502 as usize,
1503 )
1504 });
1505 {
1506 top_cursor.value_unchecked().set_obj(function);
1507 top_cursor.add(1).value_unchecked().set_obj(rb);
1508 thread.set_stack_top(top_cursor.add(2));
1509 }
1510
1511 thread.set_vm_saved_pc(pc);
1512 thread.call_tm(1, i32::from(instruction.a()))?;
1513 base = thread.stack_base();
1514 } else {
1515 thread.set_vm_saved_pc(pc);
1516 thread.do_arith_impl(ra_cursor, rb, rb, TmEvent::Unm)?;
1517 base = thread.stack_base();
1518 }
1519 }
1520 }
1521 Opcode::Length => {
1522 let ra_cursor =
1523 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1524 let ra = ra_cursor.value_unchecked();
1525 let rb = base
1526 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
1527 .value_unchecked();
1528
1529 if rb.is_table() {
1530 let table = { rb.table_value() };
1531 if thread.fast_notm(table.metatable(), TmEvent::Len) {
1532 {
1533 ra.set_number(table.getn() as f64);
1534 }
1535 } else {
1536 thread.set_vm_saved_pc(pc);
1537 thread.do_len(ra_cursor, rb)?;
1538 base = thread.stack_base();
1539 }
1540 } else if rb.is_string() {
1541 {
1542 ra.set_number(
1543 rb.string_value().as_ptr().as_ref().unwrap_unchecked().len
1544 as f64,
1545 );
1546 }
1547 } else {
1548 thread.set_vm_saved_pc(pc);
1549 thread.do_len(ra_cursor, rb)?;
1550 base = thread.stack_base();
1551 }
1552 }
1553 Opcode::NewTable => {
1554 let ra = base
1555 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1556 .value_unchecked();
1557 let b = instruction.b();
1558 let aux = { InstructionAux::new((*pc).word()) };
1559 pc = pc.add(1);
1560
1561 thread.set_vm_saved_pc(pc);
1562 let table = thread.new_table_internal(
1563 aux.word() as i32,
1564 if b == 0 {
1565 0
1566 } else {
1567 1i32 << (i32::from(b) - 1)
1568 },
1569 )?;
1570 {
1571 ra.set_table_value(table);
1572 }
1573 thread.check_gc()?;
1574 base = thread.stack_base();
1575 }
1576 Opcode::DupTable => {
1577 let ra = base
1578 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1579 .value_unchecked();
1580 let kv = proto.vm_const(constants, instruction.d() as u16 as usize);
1581 debug_assert!({ kv.is_table() });
1582
1583 thread.set_vm_saved_pc(pc);
1584 let table = thread.clone_table_internal(kv.table_value())?;
1585 {
1586 ra.set_table_value(table);
1587 }
1588 thread.check_gc()?;
1589 base = thread.stack_base();
1590 }
1591 Opcode::SetList => {
1592 let ra = base
1593 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1594 .value_unchecked();
1595 let rb = { base.add(instruction.b() as usize) };
1596 let mut c = i32::from(instruction.c()) - 1;
1597 let index = { InstructionAux::new((*pc).word()).word() as i32 };
1598 pc = pc.add(1);
1599
1600 if c == LUA_MULTRET {
1601 c = thread.stack_top().offset_from(rb) as i32;
1602 {
1603 thread.set_stack_top(thread.current_call_info().top());
1604 }
1605 }
1606
1607 if !ra.is_table() {
1608 return Ok(());
1609 }
1610
1611 let table = { ra.table_value() };
1612 let last = index + c - 1;
1613 if last > { table.as_ptr().as_ref().unwrap_unchecked().size_array } {
1614 thread.set_vm_saved_pc(pc);
1615 thread.resize_array(table, last)?;
1616 base = thread.stack_base();
1617 }
1618
1619 let array = table.array_cursor();
1620 let index = index as usize - 1;
1621 for offset in 0..c as usize {
1622 array
1623 .add(index + offset)
1624 .value_unchecked()
1625 .set_obj(rb.add(offset).value_unchecked());
1626 }
1627
1628 let table_object: GcObject = table.into();
1629 if table_object.is_black() {
1630 let table_ref = table.as_ptr().as_mut().unwrap_unchecked();
1631 thread.barrier_back(table_object, &raw mut table_ref.gc_list);
1632 }
1633 }
1634 Opcode::SubRK => {
1635 let ra_cursor =
1636 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1637 let ra = ra_cursor.value_unchecked();
1638 let kv = proto.vm_const(constants, instruction.b() as usize);
1639 let rc = base
1640 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
1641 .value_unchecked();
1642
1643 if rc.is_number() {
1644 {
1645 ra.set_number(kv.number_value() - rc.number_value());
1646 }
1647 } else {
1648 thread.set_vm_saved_pc(pc);
1649 thread.do_arith_impl(ra_cursor, kv, rc, TmEvent::Sub)?;
1650 base = thread.stack_base();
1651 }
1652 }
1653 Opcode::DivRK => {
1654 let ra_cursor =
1655 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1656 let ra = ra_cursor.value_unchecked();
1657 let kv = proto.vm_const(constants, instruction.b() as usize);
1658 let rc = base
1659 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
1660 .value_unchecked();
1661
1662 if rc.is_number() {
1663 {
1664 ra.set_number(kv.number_value() / rc.number_value());
1665 }
1666 } else if rc.is_vector() {
1667 let left = { kv.number_value() as f32 };
1668 let cx = { rc.vector_ptr() };
1669 {
1670 ra.set_vector4(
1671 left / *cx,
1672 left / *cx.add(1),
1673 left / *cx.add(2),
1674 left / vector_w_ptr(cx),
1675 );
1676 }
1677 } else {
1678 thread.set_vm_saved_pc(pc);
1679 thread.do_arith_impl(ra_cursor, kv, rc, TmEvent::Div)?;
1680 base = thread.stack_base();
1681 }
1682 }
1683 Opcode::GetUpval => {
1684 let ra = base
1685 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1686 .value_unchecked();
1687 let upref = closure.lua_upvalue_ref(instruction.b() as usize);
1688 let value = if upref.is_upvalue() {
1689 upref.upvalue_value().value()
1690 } else {
1691 upref
1692 };
1693
1694 ra.set_obj(value);
1695 }
1696 Opcode::SetUpval => {
1697 let ra = base
1698 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1699 .value_unchecked();
1700 let upref = closure.lua_upvalue_ref(instruction.b() as usize);
1701 debug_assert!(upref.is_upvalue());
1702 let upval = upref.upvalue_value();
1703
1704 upval.value().set_obj(ra);
1705
1706 let value = { ra };
1707 if value.is_collectable() {
1708 let object: GcObject = upval.into();
1709 let child = value.gc_value();
1710 if object.is_black() && child.is_white() {
1711 thread.barrier_forward(object, child);
1712 }
1713 }
1714 }
1715 Opcode::CloseUpvals => {
1716 let ra = base
1717 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1718 .value_unchecked();
1719 if let Some(open_upval) = thread.open_upvalue()
1720 && { open_upval.value_ptr() >= ra.as_ptr() }
1721 {
1722 thread.close(ra);
1723 }
1724 }
1725 Opcode::ForNPrep => {
1726 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1727 let limit = ra;
1728 let step = { ra.add(1) };
1729 let index = { ra.add(2) };
1730
1731 let non_numeric = {
1732 !limit.value_unchecked().is_number()
1733 || !step.value_unchecked().is_number()
1734 || !index.value_unchecked().is_number()
1735 };
1736 if non_numeric {
1737 thread.set_vm_saved_pc(pc);
1738 thread.prepare_forn(limit, step, index)?;
1739 }
1740
1741 let limit_value = { limit.value_unchecked().number_value() };
1742 let step_value = { step.value_unchecked().number_value() };
1743 let index_value = { index.value_unchecked().number_value() };
1744
1745 if !(if step_value > 0.0 {
1746 index_value <= limit_value
1747 } else {
1748 limit_value <= index_value
1749 }) {
1750 pc = pc.offset(i32::from(instruction.d()) as isize);
1751 }
1752 proto.assert_vm_pc(pc);
1753 }
1754 Opcode::ForNLoop => {
1755 thread.interrupt_vm(insn_pc)?;
1756 base = thread.stack_base();
1757 if flags::LuauBackedgeHeapCheck.get() && thread.needs_gc() {
1758 thread.set_vm_saved_pc(pc);
1759 thread.step(true)?;
1760 base = thread.stack_base();
1761 }
1762
1763 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1764 let limit = ra;
1765 let step = { ra.add(1) };
1766 let index = { ra.add(2) };
1767 debug_assert!(
1768 limit.value_unchecked().is_number()
1769 && step.value_unchecked().is_number()
1770 && index.value_unchecked().is_number()
1771 );
1772
1773 let limit_value = { limit.value_unchecked().number_value() };
1774 let step_value = { step.value_unchecked().number_value() };
1775 let next_index = { index.value_unchecked().number_value() } + step_value;
1776
1777 index.value_unchecked().set_number(next_index);
1778
1779 if if step_value > 0.0 {
1780 next_index <= limit_value
1781 } else {
1782 limit_value <= next_index
1783 } {
1784 pc = pc.offset(i32::from(instruction.d()) as isize);
1785 proto.assert_vm_pc(pc);
1786 }
1787 }
1788 Opcode::ForGPrep => {
1789 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1790 let ra_value = ra.value_unchecked();
1791
1792 if !{ ra_value.is_function() } {
1793 let metatable = if ra_value.is_table() {
1794 ra_value.table_value().metatable()
1795 } else if ra_value.is_userdata() {
1796 ra_value.userdata_value().metatable()
1797 } else {
1798 None
1799 };
1800
1801 let iter = if flags::DebugLuauUserDefinedClassesRuntime.get() {
1802 if let Some(function) = thread.fast_tm(metatable, TmEvent::Iter) {
1803 Some(function)
1804 } else if ra_value.is_object() {
1805 let function = thread.tm_by_obj_or_nil(ra_value, TmEvent::Iter);
1806 if function.is_nil() {
1807 thread.set_vm_saved_pc(pc);
1808 return thread
1809 .type_error(ra_value, "iterate over")
1810 .map_err(Into::into);
1811 }
1812 Some(function)
1813 } else if thread.fast_tm(metatable, TmEvent::Call).is_some() {
1814 None
1815 } else if ra_value.is_table() {
1816 ra.add(1).value_unchecked().set_obj(ra_value);
1817 ra.add(2).value_unchecked().set_light_userdata(
1818 ptr::null_mut(),
1819 USERDATA_TAG_LIMIT as i32,
1820 );
1821 ra_value.set_nil();
1822 None
1823 } else {
1824 thread.set_vm_saved_pc(pc);
1825 return thread
1826 .type_error(ra_value, "iterate over")
1827 .map_err(Into::into);
1828 }
1829 } else if let Some(function) = thread.fast_tm(metatable, TmEvent::Iter)
1830 {
1831 Some(function)
1832 } else if thread.fast_tm(metatable, TmEvent::Call).is_some() {
1833 None
1834 } else if ra_value.is_table() {
1835 ra.add(1).value_unchecked().set_obj(ra_value);
1836 ra.add(2)
1837 .value_unchecked()
1838 .set_light_userdata(ptr::null_mut(), USERDATA_TAG_LIMIT as i32);
1839 ra_value.set_nil();
1840 None
1841 } else {
1842 thread.set_vm_saved_pc(pc);
1843 return thread
1844 .type_error(ra_value, "iterate over")
1845 .map_err(Into::into);
1846 };
1847
1848 if let Some(function) = iter {
1849 ra.add(1).value_unchecked().set_obj(ra_value);
1850 ra_value.set_obj(function);
1851 thread.set_stack_top(ra.add(2));
1852 debug_assert!({
1853 thread.stack_top()
1854 <= thread.stack().add(
1855 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
1856 as usize,
1857 )
1858 });
1859
1860 thread.set_vm_saved_pc(pc);
1861 thread.call_no_yield(ra, 3)?;
1862 {
1863 thread.set_stack_top(thread.current_call_info().top());
1864 }
1865
1866 base = thread.stack_base();
1867 let ra = base
1868 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
1869 .value_unchecked();
1870 if ra.is_nil() {
1871 thread.set_vm_saved_pc(pc);
1872 return thread.type_error(ra, "call").map_err(Into::into);
1873 }
1874 }
1875 }
1876
1877 pc = insn_pc.add(1).offset(i32::from(instruction.d()) as isize);
1878 proto.assert_vm_pc(pc);
1879 }
1880 Opcode::ForGLoop => {
1881 thread.interrupt_vm(insn_pc)?;
1882 base = thread.stack_base();
1883 if flags::LuauBackedgeHeapCheck.get() && thread.needs_gc() {
1884 thread.set_vm_saved_pc(pc);
1885 thread.step(true)?;
1886 base = thread.stack_base();
1887 }
1888
1889 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1890 let ra_value = ra.value_unchecked();
1891 let aux_pc = { insn_pc.add(1) };
1892 let aux = { (*aux_pc).word() };
1893
1894 if ra_value.is_nil() && ra.add(1).value_unchecked().is_table() {
1895 let table = { ra.add(1).value_unchecked().table_value() };
1896 let mut index =
1897 { ra.add(2).value_unchecked().pointer_value() as usize as i32 };
1898 let size_array =
1899 { table.as_ptr().as_ref().unwrap_unchecked().size_array };
1900 let aux_signed = aux as i32;
1901 if aux_signed > 2 {
1902 for offset in 2..aux_signed as usize {
1903 ra.add(3 + offset).value_unchecked().set_nil();
1904 }
1905 }
1906
1907 if aux_signed < 0
1908 && ((index as u32) >= size_array as u32 || {
1909 table.array_slot(index as usize).is_nil()
1910 })
1911 {
1912 pc = aux_pc.add(1);
1913 proto.assert_vm_pc(pc);
1914 continue 'vm;
1915 }
1916
1917 while (index as u32) < size_array as u32 {
1918 let value = table.array_slot(index as usize);
1919 if !value.is_nil() {
1920 ra.add(2).value_unchecked().set_light_userdata(
1921 (index as usize + 1) as *mut (),
1922 USERDATA_TAG_LIMIT as i32,
1923 );
1924 ra.add(3).value_unchecked().set_number((index + 1) as f64);
1925 ra.add(4).value_unchecked().set_obj(value);
1926
1927 pc = aux_pc.offset(i32::from(instruction.d()) as isize);
1928 proto.assert_vm_pc(pc);
1929 break 'dispatch;
1930 }
1931
1932 index += 1;
1933 }
1934
1935 let size_node = { table.node_count() as i32 };
1936 while ((index - size_array) as u32) < size_node as u32 {
1937 let node = table.node(index - size_array);
1938 if !node.value_unchecked().is_nil() {
1939 ra.add(2).value_unchecked().set_light_userdata(
1940 (index as usize + 1) as *mut (),
1941 USERDATA_TAG_LIMIT as i32,
1942 );
1943 node.write_key_to_value(ra.add(3).value_unchecked());
1944 ra.add(4).value_unchecked().set_obj(node.value_unchecked());
1945
1946 pc = aux_pc.offset(i32::from(instruction.d()) as isize);
1947 proto.assert_vm_pc(pc);
1948 break 'dispatch;
1949 }
1950
1951 index += 1;
1952 }
1953
1954 pc = aux_pc.add(1);
1955 proto.assert_vm_pc(pc);
1956 break 'dispatch;
1957 }
1958
1959 ra.add(5)
1960 .value_unchecked()
1961 .set_obj(ra.add(2).value_unchecked());
1962 ra.add(4)
1963 .value_unchecked()
1964 .set_obj(ra.add(1).value_unchecked());
1965 ra.add(3).value_unchecked().set_obj(ra_value);
1966 thread.set_stack_top(ra.add(6));
1967 debug_assert!(
1968 thread.stack_top()
1969 <= thread.stack().add(
1970 thread.as_ptr().as_ref().unwrap_unchecked().stack_size as usize
1971 )
1972 );
1973
1974 thread.set_vm_saved_pc(aux_pc);
1975 if flags::LuauYieldIter2.get() {
1976 thread.perform_cally(ra.add(3), i32::from(aux as u8))?;
1977 } else {
1978 thread.call_no_yield(ra.add(3), i32::from(aux as u8))?;
1979 }
1980 thread.set_stack_top(thread.current_call_info().top());
1981
1982 base = thread.stack_base();
1983 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1984 ra.add(2)
1985 .value_unchecked()
1986 .set_obj(ra.add(3).value_unchecked());
1987
1988 pc = if ra.add(3).value_unchecked().is_nil() {
1989 aux_pc.add(1)
1990 } else {
1991 aux_pc.offset(i32::from(instruction.d()) as isize)
1992 };
1993 proto.assert_vm_pc(pc);
1994 }
1995 Opcode::ForGPrepInext => {
1996 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
1997 let ra_value = ra.value_unchecked();
1998 let env = closure.env();
1999
2000 if { env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 }
2001 && { ra.add(1).value_unchecked().is_table() }
2002 && { ra.add(2).value_unchecked().is_number() }
2003 && { ra.add(2).value_unchecked().number_value() == 0.0 }
2004 {
2005 {
2006 ra_value.set_nil();
2007 ra.add(2)
2008 .value_unchecked()
2009 .set_light_userdata(ptr::null_mut(), USERDATA_TAG_LIMIT as i32);
2010 }
2011 } else if !ra_value.is_function() {
2012 thread.set_vm_saved_pc(pc);
2013 return thread
2014 .type_error(ra_value, "iterate over")
2015 .map_err(Into::into);
2016 }
2017
2018 pc = insn_pc.add(1).offset(i32::from(instruction.d()) as isize);
2019 proto.assert_vm_pc(pc);
2020 }
2021 Opcode::ForGPrepNext => {
2022 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
2023 let ra_value = ra.value_unchecked();
2024 let env = closure.env();
2025
2026 if { env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 }
2027 && { ra.add(1).value_unchecked().is_table() }
2028 && { ra.add(2).value_unchecked().is_nil() }
2029 {
2030 {
2031 ra_value.set_nil();
2032 ra.add(2)
2033 .value_unchecked()
2034 .set_light_userdata(ptr::null_mut(), USERDATA_TAG_LIMIT as i32);
2035 }
2036 } else if !ra_value.is_function() {
2037 thread.set_vm_saved_pc(pc);
2038 return thread
2039 .type_error(ra_value, "iterate over")
2040 .map_err(Into::into);
2041 }
2042
2043 pc = insn_pc.add(1).offset(i32::from(instruction.d()) as isize);
2044 proto.assert_vm_pc(pc);
2045 }
2046 Opcode::GetVarargs => {
2047 let b = i32::from(instruction.b()) - 1;
2048 let n = { base.offset_from(thread.current_call_info().function()) } as i32
2049 - i32::from(proto.as_ptr().as_ref().unwrap_unchecked().num_params)
2050 - 1;
2051
2052 if b == LUA_MULTRET {
2053 thread.set_vm_saved_pc(pc);
2054 thread.check_stack_internal(n)?;
2055 base = thread.stack_base();
2056
2057 let ra =
2058 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
2059 let varargs = { base.sub(n as usize) };
2060
2061 for index in 0..n {
2062 ra.add(index as usize)
2063 .value_unchecked()
2064 .set_obj(varargs.add(index as usize).value_unchecked());
2065 }
2066
2067 thread.set_stack_top(ra.add(n as usize));
2068 } else {
2069 let ra =
2070 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
2071 let varargs = { base.sub(n.max(0) as usize) };
2072
2073 for index in 0..b.min(n) {
2074 ra.add(index as usize)
2075 .value_unchecked()
2076 .set_obj(varargs.add(index as usize).value_unchecked());
2077 }
2078 for index in n.max(0)..b {
2079 ra.add(index as usize).value_unchecked().set_nil();
2080 }
2081 }
2082 }
2083 Opcode::PrepVarargs => {
2084 let num_params = instruction.a() as usize;
2085 thread.set_vm_saved_pc(pc);
2086 thread.check_stack_internal(
2087 i32::from(closure.as_ptr().as_ref().unwrap_unchecked().stack_size)
2088 + num_params as i32,
2089 )?;
2090
2091 base = thread.stack_base();
2092 debug_assert!(thread.stack_top().offset_from(base) as usize >= num_params);
2093
2094 let fixed = base;
2095 base = thread.stack_top();
2096
2097 for index in 0..num_params {
2098 base.add(index)
2099 .value_unchecked()
2100 .set_obj(fixed.add(index).value_unchecked());
2101 fixed.add(index).value_unchecked().set_nil();
2102 }
2103
2104 let current_call_info = thread.current_call_info();
2105 let new_top = base
2106 .add(closure.as_ptr().as_ref().unwrap_unchecked().stack_size as usize);
2107 current_call_info.set_base(base);
2108 current_call_info.set_top(new_top);
2109 thread.set_stack_base(base);
2110 thread.set_stack_top(new_top);
2111 }
2112 Opcode::NativeCall => {
2113 debug_assert!(!{
2114 proto
2115 .as_ptr()
2116 .as_ref()
2117 .unwrap_unchecked()
2118 .exec_data
2119 .is_null()
2120 });
2121
2122 {
2123 let current_call_info = thread.current_call_info();
2124 current_call_info.as_ptr().as_mut().unwrap_unchecked().flags =
2125 LUA_CALLINFO_NATIVE;
2126 current_call_info.set_saved_pc(
2127 proto.as_ptr().as_ref().unwrap_unchecked().code.cast_const(),
2128 );
2129 }
2130
2131 let Some(enter) = global.execution_enter() else {
2132 unreachable!("native lvmexecute requires an execution enter callback");
2133 };
2134
2135 if { enter(thread, proto) } == 0 {
2136 return Ok(());
2137 }
2138
2139 let call_info = thread.current_call_info();
2140 closure = call_info.function_closure();
2141 proto = closure.proto().unwrap_unchecked();
2142 pc = call_info.saved_pc();
2143 base = thread.stack_base();
2144 constants = proto.constants();
2145 proto.assert_vm_pc(pc);
2146 }
2147 Opcode::Jump => {
2148 pc = pc.offset(i32::from(instruction.d()) as isize);
2149 proto.assert_vm_pc(pc);
2150 }
2151 Opcode::JumpBack => {
2152 thread.interrupt_vm(insn_pc)?;
2153 base = thread.stack_base();
2154 if flags::LuauBackedgeHeapCheck.get() && thread.needs_gc() {
2155 thread.set_vm_saved_pc(pc);
2156 thread.step(true)?;
2157 base = thread.stack_base();
2158 }
2159
2160 pc = pc.offset(i32::from(instruction.d()) as isize);
2161 proto.assert_vm_pc(pc);
2162 }
2163 Opcode::JumpIf => {
2164 let ra = base
2165 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2166 .value_unchecked();
2167 if !{ ra.is_false() } {
2168 pc = pc.offset(i32::from(instruction.d()) as isize);
2169 }
2170 proto.assert_vm_pc(pc);
2171 }
2172 Opcode::JumpIfNot => {
2173 let ra = base
2174 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2175 .value_unchecked();
2176 if ra.is_false() {
2177 pc = pc.offset(i32::from(instruction.d()) as isize);
2178 }
2179 proto.assert_vm_pc(pc);
2180 }
2181 Opcode::JumpIfEq => {
2182 let aux = { (*pc).word() };
2183 let ra = base
2184 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2185 .value_unchecked();
2186 let rb = base
2187 .vm_reg_cursor(thread.stack_top(), aux as usize)
2188 .value_unchecked();
2189
2190 if ra.tt() == rb.tt() {
2191 match ra.tt() {
2192 x if x == LUA_TNIL => {
2193 pc = pc.offset(i32::from(instruction.d()) as isize);
2194 }
2195 x if x == LUA_TBOOLEAN => {
2196 pc = if ra.boolean_value() == rb.boolean_value() {
2197 pc.offset(i32::from(instruction.d()) as isize)
2198 } else {
2199 pc.add(1)
2200 };
2201 }
2202 x if x == LUA_TLIGHTUSERDATA => {
2203 pc = if ra.pointer_value() == rb.pointer_value()
2204 && ra.light_userdata_tag() == rb.light_userdata_tag()
2205 {
2206 pc.offset(i32::from(instruction.d()) as isize)
2207 } else {
2208 pc.add(1)
2209 };
2210 }
2211 x if x == LUA_TNUMBER => {
2212 pc = if ra.number_value() == rb.number_value() {
2213 pc.offset(i32::from(instruction.d()) as isize)
2214 } else {
2215 pc.add(1)
2216 };
2217 }
2218 x if x == LUA_TVECTOR => {
2219 pc = if ra.vector_eq(rb) {
2220 pc.offset(i32::from(instruction.d()) as isize)
2221 } else {
2222 pc.add(1)
2223 };
2224 }
2225 LUA_TSTRING | LUA_TFUNCTION | LUA_TTHREAD | LUA_TBUFFER => {
2226 pc = if ra.gc_value() == rb.gc_value() {
2227 pc.offset(i32::from(instruction.d()) as isize)
2228 } else {
2229 pc.add(1)
2230 };
2231 }
2232 x if x == LUA_TTABLE => {
2233 let left = { ra.table_value() };
2234 let right = { rb.table_value() };
2235
2236 if left.metatable() == right.metatable() {
2237 let function =
2238 thread.fast_tm(left.metatable(), TmEvent::Eq);
2239
2240 if function.is_none() {
2241 pc = if left == right {
2242 pc.offset(i32::from(instruction.d()) as isize)
2243 } else {
2244 pc.add(1)
2245 };
2246 proto.assert_vm_pc(pc);
2247 break 'dispatch;
2248 }
2249 }
2250
2251 thread.set_vm_saved_pc(pc);
2252 let result = thread.equal_value(ra, rb)?;
2253
2254 pc = if result == 1 {
2255 pc.offset(i32::from(instruction.d()) as isize)
2256 } else {
2257 pc.add(1)
2258 };
2259 base = thread.stack_base();
2260 }
2261 x if x == LUA_TUSERDATA => {
2262 let left = { ra.userdata_value() };
2263 let right = { rb.userdata_value() };
2264
2265 if left.metatable() == right.metatable() {
2266 let function =
2267 thread.fast_tm(left.metatable(), TmEvent::Eq);
2268
2269 if function.is_none() {
2270 pc = if left == right {
2271 pc.offset(i32::from(instruction.d()) as isize)
2272 } else {
2273 pc.add(1)
2274 };
2275 proto.assert_vm_pc(pc);
2276 break 'dispatch;
2277 } else if let Some(function) = function
2278 && {
2279 function.is_function()
2280 && function.is_native_function()
2281 }
2282 {
2283 let top_cursor = thread.stack_top();
2284 debug_assert!({
2285 top_cursor.add(3)
2286 < thread.stack().add(
2287 thread
2288 .as_ptr()
2289 .as_ref()
2290 .unwrap_unchecked()
2291 .stack_size
2292 as usize,
2293 )
2294 });
2295
2296 let result = { top_cursor.offset_from(base) as i32 };
2297 {
2298 top_cursor.value_unchecked().set_obj(function);
2299 top_cursor.add(1).value_unchecked().set_obj(ra);
2300 top_cursor.add(2).value_unchecked().set_obj(rb);
2301 thread.set_stack_top(top_cursor.add(3));
2302 }
2303
2304 thread.set_vm_saved_pc(pc);
2305 thread.call_tm(2, result)?;
2306 base = thread.stack_base();
2307
2308 let result_slot = { base.add(result as usize) };
2309 pc = if !result_slot.value_unchecked().is_false() {
2310 pc.offset(i32::from(instruction.d()) as isize)
2311 } else {
2312 pc.add(1)
2313 };
2314 proto.assert_vm_pc(pc);
2315 break 'dispatch;
2316 }
2317 }
2318
2319 thread.set_vm_saved_pc(pc);
2320 let result = thread.equal_value(ra, rb)?;
2321
2322 pc = if result == 1 {
2323 pc.offset(i32::from(instruction.d()) as isize)
2324 } else {
2325 pc.add(1)
2326 };
2327 base = thread.stack_base();
2328 }
2329 x if x == LUA_TCLASS => {
2330 pc = if ra.class_value() == rb.class_value() {
2331 pc.offset(i32::from(instruction.d()) as isize)
2332 } else {
2333 pc.add(1)
2334 };
2335 }
2336 x if x == LUA_TOBJECT => {
2337 thread.set_vm_saved_pc(pc);
2338 let result = thread.equal_value(ra, rb)?;
2339
2340 pc = if result == 1 {
2341 pc.offset(i32::from(instruction.d()) as isize)
2342 } else {
2343 pc.add(1)
2344 };
2345 base = thread.stack_base();
2346 }
2347 x if x == LUA_TINTEGER => {
2348 pc = if ra.integer_value() == rb.integer_value() {
2349 pc.offset(i32::from(instruction.d()) as isize)
2350 } else {
2351 pc.add(1)
2352 };
2353 }
2354 _ => unreachable!("unknown jump equality type"),
2355 }
2356 } else {
2357 pc = pc.add(1);
2358 }
2359
2360 proto.assert_vm_pc(pc);
2361 }
2362 Opcode::JumpIfNotEq => {
2363 let aux = { (*pc).word() };
2364 let ra = base
2365 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2366 .value_unchecked();
2367 let rb = base
2368 .vm_reg_cursor(thread.stack_top(), aux as usize)
2369 .value_unchecked();
2370
2371 if ra.tt() == rb.tt() {
2372 match ra.tt() {
2373 x if x == LUA_TNIL => {
2374 pc = pc.add(1);
2375 }
2376 x if x == LUA_TBOOLEAN => {
2377 pc = if ra.boolean_value() != rb.boolean_value() {
2378 pc.offset(i32::from(instruction.d()) as isize)
2379 } else {
2380 pc.add(1)
2381 };
2382 }
2383 x if x == LUA_TLIGHTUSERDATA => {
2384 pc = if ra.pointer_value() != rb.pointer_value()
2385 || ra.light_userdata_tag() != rb.light_userdata_tag()
2386 {
2387 pc.offset(i32::from(instruction.d()) as isize)
2388 } else {
2389 pc.add(1)
2390 };
2391 }
2392 x if x == LUA_TNUMBER => {
2393 pc = if ra.number_value() != rb.number_value() {
2394 pc.offset(i32::from(instruction.d()) as isize)
2395 } else {
2396 pc.add(1)
2397 };
2398 }
2399 x if x == LUA_TVECTOR => {
2400 pc = if !ra.vector_eq(rb) {
2401 pc.offset(i32::from(instruction.d()) as isize)
2402 } else {
2403 pc.add(1)
2404 };
2405 }
2406 LUA_TSTRING | LUA_TFUNCTION | LUA_TTHREAD | LUA_TBUFFER => {
2407 pc = if ra.gc_value() != rb.gc_value() {
2408 pc.offset(i32::from(instruction.d()) as isize)
2409 } else {
2410 pc.add(1)
2411 };
2412 }
2413 x if x == LUA_TTABLE => {
2414 let left = { ra.table_value() };
2415 let right = { rb.table_value() };
2416
2417 if left.metatable() == right.metatable() {
2418 let function =
2419 thread.fast_tm(left.metatable(), TmEvent::Eq);
2420
2421 if function.is_none() {
2422 pc = if left != right {
2423 pc.offset(i32::from(instruction.d()) as isize)
2424 } else {
2425 pc.add(1)
2426 };
2427 proto.assert_vm_pc(pc);
2428 break 'dispatch;
2429 }
2430 }
2431
2432 thread.set_vm_saved_pc(pc);
2433 let result = thread.equal_value(ra, rb)?;
2434
2435 pc = if result == 0 {
2436 pc.offset(i32::from(instruction.d()) as isize)
2437 } else {
2438 pc.add(1)
2439 };
2440 base = thread.stack_base();
2441 }
2442 x if x == LUA_TUSERDATA => {
2443 let left = { ra.userdata_value() };
2444 let right = { rb.userdata_value() };
2445
2446 if left.metatable() == right.metatable() {
2447 let function =
2448 thread.fast_tm(left.metatable(), TmEvent::Eq);
2449
2450 if function.is_none() {
2451 pc = if left != right {
2452 pc.offset(i32::from(instruction.d()) as isize)
2453 } else {
2454 pc.add(1)
2455 };
2456 proto.assert_vm_pc(pc);
2457 break 'dispatch;
2458 } else if let Some(function) = function
2459 && {
2460 function.is_function()
2461 && function.is_native_function()
2462 }
2463 {
2464 let top_cursor = thread.stack_top();
2465 debug_assert!({
2466 top_cursor.add(3)
2467 < thread.stack().add(
2468 thread
2469 .as_ptr()
2470 .as_ref()
2471 .unwrap_unchecked()
2472 .stack_size
2473 as usize,
2474 )
2475 });
2476
2477 let result = { top_cursor.offset_from(base) as i32 };
2478 {
2479 top_cursor.value_unchecked().set_obj(function);
2480 top_cursor.add(1).value_unchecked().set_obj(ra);
2481 top_cursor.add(2).value_unchecked().set_obj(rb);
2482 thread.set_stack_top(top_cursor.add(3));
2483 }
2484
2485 thread.set_vm_saved_pc(pc);
2486 thread.call_tm(2, result)?;
2487 base = thread.stack_base();
2488
2489 let result_slot = { base.add(result as usize) };
2490 pc = if result_slot.value_unchecked().is_false() {
2491 pc.offset(i32::from(instruction.d()) as isize)
2492 } else {
2493 pc.add(1)
2494 };
2495 proto.assert_vm_pc(pc);
2496 break 'dispatch;
2497 }
2498 }
2499
2500 thread.set_vm_saved_pc(pc);
2501 let result = thread.equal_value(ra, rb)?;
2502
2503 pc = if result == 0 {
2504 pc.offset(i32::from(instruction.d()) as isize)
2505 } else {
2506 pc.add(1)
2507 };
2508 base = thread.stack_base();
2509 }
2510 x if x == LUA_TCLASS => {
2511 pc = if ra.class_value() != rb.class_value() {
2512 pc.offset(i32::from(instruction.d()) as isize)
2513 } else {
2514 pc.add(1)
2515 };
2516 }
2517 x if x == LUA_TOBJECT => {
2518 thread.set_vm_saved_pc(pc);
2519 let result = thread.equal_value(ra, rb)?;
2520
2521 pc = if result == 0 {
2522 pc.offset(i32::from(instruction.d()) as isize)
2523 } else {
2524 pc.add(1)
2525 };
2526 base = thread.stack_base();
2527 }
2528 x if x == LUA_TINTEGER => {
2529 pc = if ra.integer_value() != rb.integer_value() {
2530 pc.offset(i32::from(instruction.d()) as isize)
2531 } else {
2532 pc.add(1)
2533 };
2534 }
2535 _ => unreachable!("unknown jump equality type"),
2536 }
2537 } else {
2538 pc = pc.offset(i32::from(instruction.d()) as isize);
2539 }
2540
2541 proto.assert_vm_pc(pc);
2542 }
2543 Opcode::JumpIfLe => {
2544 let aux = { (*pc).word() };
2545 let ra = base
2546 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2547 .value_unchecked();
2548 let rb = base
2549 .vm_reg_cursor(thread.stack_top(), aux as usize)
2550 .value_unchecked();
2551
2552 if ra.is_number() && rb.is_number() {
2553 pc = if ra.number_value() <= rb.number_value() {
2554 pc.offset(i32::from(instruction.d()) as isize)
2555 } else {
2556 pc.add(1)
2557 };
2558 } else if ra.is_string() && rb.is_string() {
2559 pc = if string_compare(ra.string_value(), rb.string_value()) <= 0 {
2560 pc.offset(i32::from(instruction.d()) as isize)
2561 } else {
2562 pc.add(1)
2563 };
2564 } else {
2565 thread.set_vm_saved_pc(pc);
2566 let result = thread.less_equal(ra, rb)?;
2567 pc = if result == 1 {
2568 pc.offset(i32::from(instruction.d()) as isize)
2569 } else {
2570 pc.add(1)
2571 };
2572 base = thread.stack_base();
2573 }
2574
2575 proto.assert_vm_pc(pc);
2576 }
2577 Opcode::JumpIfNotLe => {
2578 let aux = { (*pc).word() };
2579 let ra = base
2580 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2581 .value_unchecked();
2582 let rb = base
2583 .vm_reg_cursor(thread.stack_top(), aux as usize)
2584 .value_unchecked();
2585
2586 if ra.is_number() && rb.is_number() {
2587 pc = if ra.number_value() <= rb.number_value() {
2588 pc.add(1)
2589 } else {
2590 pc.offset(i32::from(instruction.d()) as isize)
2591 };
2592 } else if ra.is_string() && rb.is_string() {
2593 pc = if string_compare(ra.string_value(), rb.string_value()) > 0 {
2594 pc.offset(i32::from(instruction.d()) as isize)
2595 } else {
2596 pc.add(1)
2597 };
2598 } else {
2599 thread.set_vm_saved_pc(pc);
2600 let result = thread.less_equal(ra, rb)?;
2601 pc = if result == 0 {
2602 pc.offset(i32::from(instruction.d()) as isize)
2603 } else {
2604 pc.add(1)
2605 };
2606 base = thread.stack_base();
2607 }
2608
2609 proto.assert_vm_pc(pc);
2610 }
2611 Opcode::JumpIfLt => {
2612 let aux = { (*pc).word() };
2613 let ra = base
2614 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2615 .value_unchecked();
2616 let rb = base
2617 .vm_reg_cursor(thread.stack_top(), aux as usize)
2618 .value_unchecked();
2619
2620 if ra.is_number() && rb.is_number() {
2621 pc = if ra.number_value() < rb.number_value() {
2622 pc.offset(i32::from(instruction.d()) as isize)
2623 } else {
2624 pc.add(1)
2625 };
2626 } else if ra.is_string() && rb.is_string() {
2627 pc = if string_compare(ra.string_value(), rb.string_value()) < 0 {
2628 pc.offset(i32::from(instruction.d()) as isize)
2629 } else {
2630 pc.add(1)
2631 };
2632 } else {
2633 thread.set_vm_saved_pc(pc);
2634 let result = thread.less_than_internal(ra, rb)?;
2635 pc = if result == 1 {
2636 pc.offset(i32::from(instruction.d()) as isize)
2637 } else {
2638 pc.add(1)
2639 };
2640 base = thread.stack_base();
2641 }
2642
2643 proto.assert_vm_pc(pc);
2644 }
2645 Opcode::JumpIfNotLt => {
2646 let aux = { *pc };
2647 let ra = base
2648 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2649 .value_unchecked();
2650 let rb = base
2651 .vm_reg_cursor(thread.stack_top(), aux as usize)
2652 .value_unchecked();
2653
2654 if ra.is_number() && rb.is_number() {
2655 pc = if ra.number_value() < rb.number_value() {
2656 pc.add(1)
2657 } else {
2658 pc.offset(i32::from(instruction.d()) as isize)
2659 };
2660 } else if ra.is_string() && rb.is_string() {
2661 pc = if string_compare(ra.string_value(), rb.string_value()) >= 0 {
2662 pc.offset(i32::from(instruction.d()) as isize)
2663 } else {
2664 pc.add(1)
2665 };
2666 } else {
2667 thread.set_vm_saved_pc(pc);
2668 let result = thread.less_than_internal(ra, rb)?;
2669 pc = if result == 0 {
2670 pc.offset(i32::from(instruction.d()) as isize)
2671 } else {
2672 pc.add(1)
2673 };
2674 base = thread.stack_base();
2675 }
2676
2677 proto.assert_vm_pc(pc);
2678 }
2679 Opcode::LoadKx => {
2680 let ra = base
2681 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2682 .value_unchecked();
2683 let aux = { InstructionAux::new((*pc).word()) };
2684 pc = pc.add(1);
2685 let kv = proto.vm_const(constants, aux.word() as usize);
2686 {
2687 ra.set_obj(kv);
2688 }
2689 }
2690 Opcode::GetImport => {
2691 let ra_cursor =
2692 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
2693 let ra = ra_cursor.value_unchecked();
2694 let kv = proto.vm_const(constants, instruction.d() as u16 as usize);
2695 let env = closure.env();
2696
2697 if { !kv.is_nil() } && {
2698 env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0
2699 } {
2700 {
2701 ra.set_obj(kv);
2702 }
2703 pc = pc.add(1);
2704 proto.assert_vm_pc(pc);
2705 } else {
2706 let aux = { InstructionAux::new((*pc).word()) };
2707 pc = pc.add(1);
2708
2709 thread.set_vm_saved_pc(pc);
2710 thread.get_import(env, constants, ra_cursor, aux.word(), false)?;
2711 base = thread.stack_base();
2712 }
2713 }
2714 Opcode::GetTableKs => {
2715 let ra_cursor =
2716 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
2717 let ra = ra_cursor.value_unchecked();
2718 let rb = base
2719 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
2720 .value_unchecked();
2721 let aux = { InstructionAux::new((*pc).word()) };
2722 pc = pc.add(1);
2723 let kv = proto.vm_const(constants, aux.word() as usize);
2724 debug_assert!({ kv.is_string() });
2725 let kv_string = { kv.string_value() };
2726
2727 if rb.is_table() {
2728 let table = { rb.table_value() };
2729 let slot = instruction.c() as usize & table.node_mask_8();
2730 let node = { table.node(slot as i32) };
2731
2732 if node.has_string_key(kv_string) && !node.value_is_nil() {
2733 ra.set_obj(node.value_unchecked());
2734 } else if table.metatable().is_none() {
2735 if let Some(node_cursor) = table.get_str_node(kv_string) {
2736 let cached_slot = { table.node_index(node_cursor) };
2737 patch_instruction_c(
2738 insn_pc as *mut Instruction,
2739 cached_slot as u8,
2740 );
2741 ra.set_obj(node_cursor.node_unchecked().value_unchecked());
2742 } else {
2743 ra.set_nil();
2744 }
2745 } else {
2746 thread.set_cached_slot(slot as i32);
2747 thread.set_vm_saved_pc(pc);
2748 thread.get_table_internal(rb, kv, ra_cursor)?;
2749 let cached_slot = thread.cached_slot();
2750 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
2751 base = thread.stack_base();
2752 }
2753 } else {
2754 let direct_field = flags::LuauDirectFieldGet.get();
2755 if direct_field && { rb.is_userdata() } {
2756 let userdata = { rb.userdata_value() };
2757 if let Some(dispatch) = global.userdata_direct_field(
2758 userdata.as_ptr().as_ref().unwrap_unchecked().tag as usize,
2759 ) {
2760 let slot = instruction.c() as usize & dispatch.node_mask_8();
2761 let node = { dispatch.node(slot as i32) };
2762
2763 if node.has_string_key(kv_string) && !node.value_is_nil() {
2764 let function: LuaUserdataDirectFieldGet =
2765 std::mem::transmute(
2766 node.value_unchecked().pointer_value(),
2767 );
2768 let mut result = UserdataDirectFieldResult(ra);
2769 {
2770 function(userdata.data_mut_ptr().cast(), &mut result);
2771 }
2772 break 'dispatch;
2773 }
2774
2775 if let Some(node_cursor) = dispatch.get_str_node(kv_string) {
2776 let cached_slot = dispatch.node_index(node_cursor);
2777 patch_instruction_c(
2778 insn_pc as *mut Instruction,
2779 cached_slot as u8,
2780 );
2781
2782 let function: LuaUserdataDirectFieldGet =
2783 std::mem::transmute(
2784 node_cursor
2785 .node_unchecked()
2786 .value_unchecked()
2787 .pointer_value(),
2788 );
2789 let mut result = UserdataDirectFieldResult(ra);
2790 {
2791 function(userdata.data_mut_ptr().cast(), &mut result);
2792 }
2793 break 'dispatch;
2794 }
2795 }
2796 }
2797
2798 if rb.is_userdata() {
2799 let userdata = { rb.userdata_value() };
2800 if let Some(function) =
2801 thread.fast_tm(userdata.metatable(), TmEvent::Index)
2802 && { function.is_function() && function.is_native_function() }
2803 {
2804 let top_cursor = thread.stack_top();
2805 debug_assert!({
2806 top_cursor.add(3)
2807 < thread.stack().add(
2808 thread
2809 .as_ptr()
2810 .as_ref()
2811 .unwrap_unchecked()
2812 .stack_size
2813 as usize,
2814 )
2815 });
2816 {
2817 top_cursor.value_unchecked().set_obj(function);
2818 top_cursor.add(1).value_unchecked().set_obj(rb);
2819 top_cursor.add(2).value_unchecked().set_obj(kv);
2820 thread.set_stack_top(top_cursor.add(3));
2821 thread.set_cached_slot(i32::from(instruction.c()));
2822 }
2823
2824 thread.set_vm_saved_pc(pc);
2825 thread.call_tm(2, i32::from(instruction.a()))?;
2826 let cached_slot = thread.cached_slot();
2827 patch_instruction_c(
2828 insn_pc as *mut Instruction,
2829 cached_slot as u8,
2830 );
2831 base = thread.stack_base();
2832 break 'dispatch;
2833 }
2834 } else if rb.is_vector() {
2835 let name = { kv_string.as_bytes() };
2836 if name.len() == 1 {
2837 let component = match name[0] | b' ' {
2838 b'x' => Some(0usize),
2839 b'y' => Some(1usize),
2840 b'z' => Some(2usize),
2841 #[cfg(feature = "vector4")]
2842 b'w' => Some(3usize),
2843 _ => None,
2844 };
2845
2846 if let Some(component) = component
2847 && component < LUA_VECTOR_SIZE
2848 {
2849 let vector = { rb.vector_ptr() };
2850 {
2851 ra.set_number(match component {
2852 0 => *vector,
2853 1 => *vector.add(1),
2854 2 => *vector.add(2),
2855 #[cfg(feature = "vector4")]
2856 3 => vector_w_ptr(vector),
2857 _ => unreachable!(),
2858 }
2859 as f64);
2860 }
2861 break 'dispatch;
2862 }
2863 }
2864
2865 let vector_metatable = global.metatable(LUA_TVECTOR as usize);
2866 if let Some(function) =
2867 thread.fast_tm(vector_metatable, TmEvent::Index)
2868 && { function.is_function() && function.is_native_function() }
2869 {
2870 let top_cursor = thread.stack_top();
2871 debug_assert!({
2872 top_cursor.add(3)
2873 < thread.stack().add(
2874 thread
2875 .as_ptr()
2876 .as_ref()
2877 .unwrap_unchecked()
2878 .stack_size
2879 as usize,
2880 )
2881 });
2882 {
2883 top_cursor.value_unchecked().set_obj(function);
2884 top_cursor.add(1).value_unchecked().set_obj(rb);
2885 top_cursor.add(2).value_unchecked().set_obj(kv);
2886 thread.set_stack_top(top_cursor.add(3));
2887 thread.set_cached_slot(i32::from(instruction.c()));
2888 }
2889
2890 thread.set_vm_saved_pc(pc);
2891 thread.call_tm(2, i32::from(instruction.a()))?;
2892 let cached_slot = thread.cached_slot();
2893 patch_instruction_c(
2894 insn_pc as *mut Instruction,
2895 cached_slot as u8,
2896 );
2897 base = thread.stack_base();
2898 break 'dispatch;
2899 }
2900 } else if flags::DebugLuauUserDefinedClassesRuntime.get() && {
2901 rb.is_object()
2902 } {
2903 let instance = { rb.object_value() };
2904 match instance.lookup_member_cached(kv_string, instruction.c()) {
2905 ObjectMemberLookup::Cached(member) => {
2906 ra.set_obj(member);
2907 }
2908 ObjectMemberLookup::Resolved { offset, member } => {
2909 ra.set_obj(member);
2910 patch_instruction_c(
2911 insn_pc as *mut Instruction,
2912 offset as u8,
2913 );
2914 }
2915 ObjectMemberLookup::Missing => {
2916 return thread
2917 .missing_member_error(rb, kv)
2918 .map_err(Into::into);
2919 }
2920 }
2921 break 'dispatch;
2922 } else {
2923 thread.set_vm_saved_pc(pc);
2924 thread.get_table_internal(rb, kv, ra_cursor)?;
2925 base = thread.stack_base();
2926 }
2927 }
2928 }
2929 Opcode::SetTableKs => {
2930 let ra = base
2931 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
2932 .value_unchecked();
2933 let rb = base
2934 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
2935 .value_unchecked();
2936 let aux = { InstructionAux::new((*pc).word()) };
2937 pc = pc.add(1);
2938 let kv = proto.vm_const(constants, aux.word() as usize);
2939 debug_assert!({ kv.is_string() });
2940 let kv_string = { kv.string_value() };
2941
2942 if rb.is_table() {
2943 let table = { rb.table_value() };
2944 let slot = instruction.c() as usize & table.node_mask_8();
2945 let node = { table.node(slot as i32) };
2946
2947 if node.has_string_key(kv_string)
2948 && !node.value_is_nil()
2949 && table.as_ptr().as_ref().unwrap_unchecked().readonly == 0
2950 {
2951 node.value_unchecked().set_obj(ra);
2952 if ra.is_collectable() {
2953 let table_object: GcObject = table.into();
2954 let child = ra.gc_value();
2955 if table_object.is_black() && child.is_white() {
2956 thread.barrier_table(table, child);
2957 }
2958 }
2959 } else if thread.fast_notm(table.metatable(), TmEvent::NewIndex)
2960 && table.as_ptr().as_ref().unwrap_unchecked().readonly == 0
2961 {
2962 thread.set_vm_saved_pc(pc);
2963 let node_cursor = thread.set_str(table, kv_string)?;
2964 let cached_slot = { table.node_index(node_cursor) };
2965 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
2966 node_cursor.node_unchecked().value_unchecked().set_obj(ra);
2967 if ra.is_collectable() {
2968 let table_object: GcObject = table.into();
2969 let child = ra.gc_value();
2970 if table_object.is_black() && child.is_white() {
2971 thread.barrier_table(table, child);
2972 }
2973 }
2974 base = thread.stack_base();
2975 } else {
2976 thread.set_cached_slot(slot as i32);
2977 thread.set_vm_saved_pc(pc);
2978 thread.set_table_internal(rb, kv, ra)?;
2979 let cached_slot = thread.cached_slot();
2980 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
2981 base = thread.stack_base();
2982 }
2983 } else if rb.is_userdata() {
2984 let userdata = { rb.userdata_value() };
2985 if let Some(function) =
2986 thread.fast_tm(userdata.metatable(), TmEvent::NewIndex)
2987 && { function.is_function() && function.is_native_function() }
2988 {
2989 let top_cursor = thread.stack_top();
2990 debug_assert!({
2991 top_cursor.add(4)
2992 < thread.stack().add(
2993 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
2994 as usize,
2995 )
2996 });
2997 {
2998 top_cursor.value_unchecked().set_obj(function);
2999 top_cursor.add(1).value_unchecked().set_obj(rb);
3000 top_cursor.add(2).value_unchecked().set_obj(kv);
3001 top_cursor.add(3).value_unchecked().set_obj(ra);
3002 thread.set_stack_top(top_cursor.add(4));
3003 thread.set_cached_slot(i32::from(instruction.c()));
3004 }
3005
3006 thread.set_vm_saved_pc(pc);
3007 thread.call_tm(3, -1)?;
3008 let cached_slot = thread.cached_slot();
3009 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
3010 base = thread.stack_base();
3011 } else {
3012 thread.set_vm_saved_pc(pc);
3013 thread.set_table_internal(rb, kv, ra)?;
3014 base = thread.stack_base();
3015 }
3016 } else {
3017 thread.set_vm_saved_pc(pc);
3018 thread.set_table_internal(rb, kv, ra)?;
3019 base = thread.stack_base();
3020 }
3021 }
3022 Opcode::GetTable => {
3023 let ra_cursor =
3024 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3025 let ra = ra_cursor.value_unchecked();
3026 let rb = base
3027 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3028 .value_unchecked();
3029 let rc = base
3030 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
3031 .value_unchecked();
3032
3033 if rb.is_table() && rc.is_number() {
3034 let table = rb.table_value();
3035 let indexd = rc.number_value();
3036 let index = indexd as i32;
3037
3038 if (index as u32).wrapping_sub(1)
3039 < table.as_ptr().as_ref().unwrap_unchecked().size_array as u32
3040 && table
3041 .as_ptr()
3042 .as_ref()
3043 .unwrap_unchecked()
3044 .metatable
3045 .is_null()
3046 && (index as f64) == indexd
3047 {
3048 ra.set_obj(table.array_slot((index - 1) as usize));
3049 break 'dispatch;
3050 }
3051 }
3052
3053 thread.set_vm_saved_pc(pc);
3054 thread.get_table_internal(rb, rc, ra_cursor)?;
3055 base = thread.stack_base();
3056 }
3057 Opcode::SetTable => {
3058 let ra = base
3059 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
3060 .value_unchecked();
3061 let rb = base
3062 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3063 .value_unchecked();
3064 let rc = base
3065 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
3066 .value_unchecked();
3067 if rb.is_table() && rc.is_number() {
3068 let table = { rb.table_value() };
3069 let indexd = { rc.number_value() };
3070 let index = indexd as i32;
3071
3072 if (index as u32).wrapping_sub(1) < {
3073 table.as_ptr().as_ref().unwrap_unchecked().size_array as u32
3074 } && {
3075 table
3076 .as_ptr()
3077 .as_ref()
3078 .unwrap_unchecked()
3079 .metatable
3080 .is_null()
3081 } && { table.as_ptr().as_ref().unwrap_unchecked().readonly == 0 }
3082 && (index as f64) == indexd
3083 {
3084 table.array_slot((index - 1) as usize).set_obj(ra);
3085 if ra.is_collectable() {
3086 let table_object: GcObject = table.into();
3087 let child = ra.gc_value();
3088 if table_object.is_black() && child.is_white() {
3089 thread.barrier_table(table, child);
3090 }
3091 }
3092 } else {
3093 thread.set_vm_saved_pc(pc);
3094 thread.set_table_internal(rb, rc, ra)?;
3095 base = thread.stack_base();
3096 }
3097 } else {
3098 thread.set_vm_saved_pc(pc);
3099 thread.set_table_internal(rb, rc, ra)?;
3100 base = thread.stack_base();
3101 }
3102 }
3103 Opcode::GetTableN => {
3104 let ra_cursor =
3105 base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3106 let ra = ra_cursor.value_unchecked();
3107 let rb = base
3108 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3109 .value_unchecked();
3110 let c = instruction.c() as i32;
3111
3112 if rb.is_table() {
3113 let table = { rb.table_value() };
3114 if (c as u32) < {
3115 table.as_ptr().as_ref().unwrap_unchecked().size_array as u32
3116 } && {
3117 table
3118 .as_ptr()
3119 .as_ref()
3120 .unwrap_unchecked()
3121 .metatable
3122 .is_null()
3123 } {
3124 ra.set_obj(table.array_slot(c as usize));
3125 } else {
3126 let mut number_raw = RawTValue::number((c + 1) as f64);
3127 let number = TValue::from_mut(&mut number_raw);
3128 thread.set_vm_saved_pc(pc);
3129 thread.get_table_internal(rb, number, ra_cursor)?;
3130 base = thread.stack_base();
3131 }
3132 } else {
3133 let mut number_raw = RawTValue::number((c + 1) as f64);
3134 let number = TValue::from_mut(&mut number_raw);
3135 thread.set_vm_saved_pc(pc);
3136 thread.get_table_internal(rb, number, ra_cursor)?;
3137 base = thread.stack_base();
3138 }
3139 }
3140 Opcode::SetTableN => {
3141 let ra = base
3142 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
3143 .value_unchecked();
3144 let rb = base
3145 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3146 .value_unchecked();
3147 let c = instruction.c() as i32;
3148
3149 if rb.is_table() {
3150 let table = { rb.table_value() };
3151 if (c as u32) < {
3152 table.as_ptr().as_ref().unwrap_unchecked().size_array as u32
3153 } && {
3154 table
3155 .as_ptr()
3156 .as_ref()
3157 .unwrap_unchecked()
3158 .metatable
3159 .is_null()
3160 } && { table.as_ptr().as_ref().unwrap_unchecked().readonly == 0 }
3161 {
3162 table.array_slot(c as usize).set_obj(ra);
3163 if ra.is_collectable() {
3164 let table_object: GcObject = table.into();
3165 let child = ra.gc_value();
3166 if table_object.is_black() && child.is_white() {
3167 thread.barrier_table(table, child);
3168 }
3169 }
3170 } else {
3171 let mut number_raw = RawTValue::number((c + 1) as f64);
3172 let number = TValue::from_mut(&mut number_raw);
3173 thread.set_vm_saved_pc(pc);
3174 thread.set_table_internal(rb, number, ra)?;
3175 base = thread.stack_base();
3176 }
3177 } else {
3178 let mut number_raw = RawTValue::number((c + 1) as f64);
3179 let number = TValue::from_mut(&mut number_raw);
3180 thread.set_vm_saved_pc(pc);
3181 thread.set_table_internal(rb, number, ra)?;
3182 base = thread.stack_base();
3183 }
3184 }
3185 Opcode::NameCall => {
3186 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3187 let ra_value = ra.value_unchecked();
3188 let rb = base
3189 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3190 .value_unchecked();
3191 let aux = { InstructionAux::new((*pc).word()) };
3192 pc = pc.add(1);
3193 let kv = proto.vm_const(constants, aux.word() as usize);
3194 debug_assert!({ kv.is_string() });
3195 let kv_string = { kv.string_value() };
3196
3197 if rb.is_table() {
3198 let table = { rb.table_value() };
3199 let slot = {
3200 kv_string.as_ptr().as_ref().unwrap_unchecked().hash as usize
3201 & table.hash_mask()
3202 };
3203 let node = { table.node(slot as i32) };
3204
3205 if node.has_string_key(kv_string) && !node.value_is_nil() {
3206 ra.add(1).value_unchecked().set_obj(rb);
3207 ra_value.set_obj(node.value_unchecked());
3208 } else if node.next() == 0 {
3209 if let Some(index_tm) =
3210 thread.fast_tm(table.metatable(), TmEvent::Index)
3211 && index_tm.is_table()
3212 {
3213 let meta_table = { index_tm.table_value() };
3214 let meta_slot =
3215 instruction.c() as usize & meta_table.node_mask_8();
3216 let meta_node = { meta_table.node(meta_slot as i32) };
3217
3218 if meta_node.has_string_key(kv_string)
3219 && !meta_node.value_is_nil()
3220 {
3221 ra.add(1).value_unchecked().set_obj(rb);
3222 ra_value.set_obj(meta_node.value_unchecked());
3223 break 'dispatch;
3224 }
3225 }
3226
3227 {
3228 ra.add(1).value_unchecked().set_obj(rb);
3229 thread.set_cached_slot(i32::from(instruction.c()));
3230 }
3231 thread.set_vm_saved_pc(pc);
3232 thread.get_table_internal(rb, kv, ra)?;
3233 let cached_slot = thread.cached_slot();
3234 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
3235 base = thread.stack_base();
3236 let ra = base
3237 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3238 if ra.value_unchecked().is_nil() {
3239 return thread
3240 .method_error(ra.add(1).value_unchecked(), kv_string)
3241 .map_err(Into::into);
3242 }
3243 } else {
3244 {
3245 ra.add(1).value_unchecked().set_obj(rb);
3246 thread.set_cached_slot(i32::from(instruction.c()));
3247 }
3248 thread.set_vm_saved_pc(pc);
3249 thread.get_table_internal(rb, kv, ra)?;
3250 let cached_slot = thread.cached_slot();
3251 patch_instruction_c(insn_pc as *mut Instruction, cached_slot as u8);
3252 base = thread.stack_base();
3253 let ra = base
3254 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3255 if ra.value_unchecked().is_nil() {
3256 return thread
3257 .method_error(ra.add(1).value_unchecked(), kv_string)
3258 .map_err(Into::into);
3259 }
3260 }
3261 } else {
3262 let metatable = if rb.is_userdata() {
3263 rb.userdata_value().metatable()
3264 } else {
3265 global.metatable(rb.tt() as usize)
3266 };
3267
3268 if let Some(function) = thread.fast_tm(metatable, TmEvent::NameCall) {
3269 {
3270 ra.add(1).value_unchecked().set_obj(rb);
3271 ra_value.set_obj(function);
3272 thread.set_name_call(Some(kv_string));
3273 }
3274 } else if let Some(index_tm) = thread.fast_tm(metatable, TmEvent::Index)
3275 {
3276 if index_tm.is_table() {
3277 let table = { index_tm.table_value() };
3278 let slot = instruction.c() as usize & table.node_mask_8();
3279 let node = { table.node(slot as i32) };
3280
3281 if node.has_string_key(kv_string) && !node.value_is_nil() {
3282 ra.add(1).value_unchecked().set_obj(rb);
3283 ra_value.set_obj(node.value_unchecked());
3284 } else {
3285 {
3286 ra.add(1).value_unchecked().set_obj(rb);
3287 thread.set_cached_slot(slot as i32);
3288 }
3289 thread.set_vm_saved_pc(pc);
3290 thread.get_table_internal(rb, kv, ra)?;
3291 let cached_slot = thread.cached_slot();
3292 patch_instruction_c(
3293 insn_pc as *mut Instruction,
3294 cached_slot as u8,
3295 );
3296 base = thread.stack_base();
3297 let ra = base.vm_reg_cursor(
3298 thread.stack_top(),
3299 instruction.a() as usize,
3300 );
3301 if ra.value_unchecked().is_nil() {
3302 return thread
3303 .method_error(
3304 ra.add(1).value_unchecked(),
3305 kv_string,
3306 )
3307 .map_err(Into::into);
3308 }
3309 }
3310 } else if flags::DebugLuauUserDefinedClassesRuntime.get() && {
3311 rb.is_object()
3312 } {
3313 let instance = { rb.object_value() };
3314 ra.add(1).value_unchecked().set_obj(rb);
3315 match instance.lookup_member_cached(kv_string, instruction.c())
3316 {
3317 ObjectMemberLookup::Cached(member) => {
3318 ra_value.set_obj(member);
3319 }
3320 ObjectMemberLookup::Resolved { offset, member } => {
3321 ra_value.set_obj(member);
3322 patch_instruction_c(
3323 insn_pc as *mut Instruction,
3324 offset as u8,
3325 );
3326 }
3327 ObjectMemberLookup::Missing => {
3328 return thread
3329 .missing_member_error(rb, kv)
3330 .map_err(Into::into);
3331 }
3332 }
3333 } else {
3334 {
3335 ra.add(1).value_unchecked().set_obj(rb);
3336 }
3337 thread.set_vm_saved_pc(pc);
3338 thread.get_table_internal(rb, kv, ra)?;
3339 base = thread.stack_base();
3340 let ra = base.vm_reg_cursor(
3341 thread.stack_top(),
3342 instruction.a() as usize,
3343 );
3344 if ra.value_unchecked().is_nil() {
3345 return thread
3346 .method_error(ra.add(1).value_unchecked(), kv_string)
3347 .map_err(Into::into);
3348 }
3349 }
3350 } else if flags::DebugLuauUserDefinedClassesRuntime.get() && {
3351 rb.is_object()
3352 } {
3353 let instance = { rb.object_value() };
3354 ra.add(1).value_unchecked().set_obj(rb);
3355 match instance.lookup_member_cached(kv_string, instruction.c()) {
3356 ObjectMemberLookup::Cached(member) => {
3357 ra_value.set_obj(member);
3358 }
3359 ObjectMemberLookup::Resolved { offset, member } => {
3360 ra_value.set_obj(member);
3361 patch_instruction_c(
3362 insn_pc as *mut Instruction,
3363 offset as u8,
3364 );
3365 }
3366 ObjectMemberLookup::Missing => {
3367 return thread
3368 .missing_member_error(rb, kv)
3369 .map_err(Into::into);
3370 }
3371 }
3372 } else {
3373 {
3374 ra.add(1).value_unchecked().set_obj(rb);
3375 }
3376 thread.set_vm_saved_pc(pc);
3377 thread.get_table_internal(rb, kv, ra)?;
3378 base = thread.stack_base();
3379 let ra = base
3380 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3381 if ra.value_unchecked().is_nil() {
3382 return thread
3383 .method_error(ra.add(1).value_unchecked(), kv_string)
3384 .map_err(Into::into);
3385 }
3386 }
3387 }
3388
3389 #[cfg(debug_assertions)]
3390 if !flags::LuauCallFeedback.get() {
3391 assert_ne!((*pc).opcode(), Opcode::CallFb);
3392 }
3393 }
3394 Opcode::GetUDataKs => {
3395 let _ra = base
3396 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
3397 .value_unchecked();
3398 let rb = base
3399 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3400 .value_unchecked();
3401 let aux = { (*pc).word() };
3402 pc = pc.add(1);
3403 let kidx = (aux & 0xffff) as usize;
3404 let kv = proto.vm_const(constants, kidx);
3405
3406 if rb.is_userdata() {
3407 let userdata = { rb.userdata_value() };
3408 let utag =
3409 { userdata.as_ptr().as_ref().unwrap_unchecked().tag as usize };
3410 let direct = {
3411 &global.as_ptr().as_ref().unwrap_unchecked().userdata_direct[utag]
3412 };
3413 let function = direct.index;
3414 let tm = TValue::from_ref(&direct.index_tm);
3415
3416 if let Some(function) = function
3417 && !tm.is_nil()
3418 {
3419 let udata = { userdata.data_mut_ptr() };
3420 let top_cursor = thread.stack_top();
3421 debug_assert!({
3422 top_cursor.add(3)
3423 < thread.stack().add(
3424 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
3425 as usize,
3426 )
3427 });
3428 {
3429 top_cursor.value_unchecked().set_obj(tm);
3430 top_cursor.add(1).value_unchecked().set_obj(rb);
3431 top_cursor.add(2).value_unchecked().set_obj(kv);
3432 thread.set_stack_top(top_cursor.add(3));
3433 }
3434
3435 thread.set_vm_saved_pc(pc);
3436 {
3437 thread.increment_native_call_depth();
3438 }
3439 if thread
3440 .as_ptr()
3441 .as_ref()
3442 .unwrap_unchecked()
3443 .native_call_depth
3444 >= crate::thread::LUAI_MAX_NATIVE_CALLS
3445 {
3446 thread.check_c_stack()?;
3447 }
3448
3449 thread.setup_cci(1, top_cursor)?;
3450
3451 let mut cached_slot = (aux >> 16) as u16;
3452 {
3453 function(
3454 thread,
3455 udata.cast(),
3456 i32::from(
3457 kv.string_value()
3458 .as_ptr()
3459 .as_ref()
3460 .unwrap_unchecked()
3461 .atom,
3462 ),
3463 &mut cached_slot,
3464 utag as i32,
3465 )?;
3466 }
3467
3468 if cached_slot != (aux >> 16) as u16
3469 && decode_instruction_opcode(*insn_pc) == Opcode::GetUDataKs
3470 {
3471 patch_aux_slot(
3472 pc.sub(1) as *mut Instruction,
3473 kidx as u16,
3474 cached_slot,
3475 );
3476 }
3477
3478 let parent_cursor = thread.current_call_info_cursor().sub(1);
3479 let parent = parent_cursor.call_info_unchecked();
3480
3481 {
3482 thread.set_current_call_info(parent_cursor);
3483 thread.set_stack_base(parent.base());
3484 thread.decrement_native_call_depth();
3485 }
3486
3487 base = thread.stack_base();
3488 let ra = base
3489 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
3490 .value_unchecked();
3491 {
3492 let result_cursor = thread.stack_top().sub(1);
3493 ra.set_obj(result_cursor.value_unchecked());
3494 thread.set_stack_top(parent.top());
3495 }
3496 break 'dispatch;
3497 }
3498 }
3499
3500 patch_instruction_opcode(insn_pc as *mut Instruction, Opcode::GetTableKs);
3501 patch_aux_slot(pc.sub(1) as *mut Instruction, kidx as u16, 0);
3502 pc = insn_pc.add(1);
3503 dispatch_opcode = Opcode::GetTableKs;
3504 continue 'dispatch;
3505 }
3506 Opcode::SetUDataKs => {
3507 let ra = base
3508 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
3509 .value_unchecked();
3510 let rb = base
3511 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3512 .value_unchecked();
3513 let aux = { (*pc).word() };
3514 pc = pc.add(1);
3515 let kidx = (aux & 0xffff) as usize;
3516 let kv = proto.vm_const(constants, kidx);
3517
3518 if rb.is_userdata() {
3519 let userdata = { rb.userdata_value() };
3520 let utag =
3521 { userdata.as_ptr().as_ref().unwrap_unchecked().tag as usize };
3522 let direct = {
3523 &global.as_ptr().as_ref().unwrap_unchecked().userdata_direct[utag]
3524 };
3525 let function = direct.new_index;
3526 let tm = TValue::from_ref(&direct.new_index_tm);
3527
3528 if let Some(function) = function
3529 && !tm.is_nil()
3530 {
3531 let udata = { userdata.data_mut_ptr() };
3532 let top_cursor = thread.stack_top();
3533 debug_assert!({
3534 top_cursor.add(4)
3535 < thread.stack().add(
3536 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
3537 as usize,
3538 )
3539 });
3540 {
3541 top_cursor.value_unchecked().set_obj(tm);
3542 top_cursor.add(1).value_unchecked().set_obj(rb);
3543 top_cursor.add(2).value_unchecked().set_obj(kv);
3544 top_cursor.add(3).value_unchecked().set_obj(ra);
3545 thread.set_stack_top(top_cursor.add(4));
3546 }
3547
3548 thread.set_vm_saved_pc(pc);
3549 {
3550 thread.increment_native_call_depth();
3551 }
3552 if thread
3553 .as_ptr()
3554 .as_ref()
3555 .unwrap_unchecked()
3556 .native_call_depth
3557 >= crate::thread::LUAI_MAX_NATIVE_CALLS
3558 {
3559 thread.check_c_stack()?;
3560 }
3561
3562 thread.setup_cci(0, top_cursor)?;
3563
3564 let mut cached_slot = (aux >> 16) as u16;
3565 {
3566 function(
3567 thread,
3568 udata.cast(),
3569 i32::from(
3570 kv.string_value()
3571 .as_ptr()
3572 .as_ref()
3573 .unwrap_unchecked()
3574 .atom,
3575 ),
3576 &mut cached_slot,
3577 utag as i32,
3578 )?;
3579 }
3580
3581 if cached_slot != (aux >> 16) as u16
3582 && decode_instruction_opcode(*insn_pc) == Opcode::SetUDataKs
3583 {
3584 patch_aux_slot(
3585 pc.sub(1) as *mut Instruction,
3586 kidx as u16,
3587 cached_slot,
3588 );
3589 }
3590
3591 let parent = thread.current_call_info_cursor().sub(1);
3592
3593 {
3594 let parent_call_info = parent.call_info_unchecked();
3595 thread.set_current_call_info(parent);
3596 thread.set_stack_base(parent_call_info.base());
3597 thread.set_stack_top(parent_call_info.top());
3598 thread.decrement_native_call_depth();
3599 }
3600 base = thread.stack_base();
3601 break 'dispatch;
3602 }
3603 }
3604
3605 patch_instruction_opcode(insn_pc as *mut Instruction, Opcode::SetTableKs);
3606 patch_aux_slot(pc.sub(1) as *mut Instruction, kidx as u16, 0);
3607 pc = insn_pc.add(1);
3608 dispatch_opcode = Opcode::SetTableKs;
3609 continue 'dispatch;
3610 }
3611 Opcode::NameCallUData => {
3612 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
3613 let ra_value = ra.value_unchecked();
3614 let rb = base
3615 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3616 .value_unchecked();
3617 let aux = { (*pc).word() };
3618 let namecall_slot = pc;
3619 pc = pc.add(1);
3620 let kidx = (aux & 0xffff) as usize;
3621 let kv = proto.vm_const(constants, kidx);
3622
3623 if rb.is_userdata() {
3624 let userdata = { rb.userdata_value() };
3625 let utag =
3626 { userdata.as_ptr().as_ref().unwrap_unchecked().tag as usize };
3627 let direct = {
3628 &global.as_ptr().as_ref().unwrap_unchecked().userdata_direct[utag]
3629 };
3630 let function = direct.name_call;
3631 let tm = TValue::from_ref(&direct.name_call_tm);
3632
3633 if let Some(function) = function
3634 && !tm.is_nil()
3635 {
3636 let udata = { userdata.data_mut_ptr() };
3637 {
3638 ra.add(1).value_unchecked().set_obj(rb);
3639 ra_value.set_obj(tm);
3640 }
3641
3642 debug_assert!(matches!(
3643 { (*pc).opcode() },
3644 Opcode::Call | Opcode::CallFb
3645 ));
3646 let call_instruction = { *pc };
3647 pc = pc.add(1);
3648 if flags::LuauCallFeedback.get()
3649 && call_instruction.opcode() == Opcode::CallFb
3650 {
3651 pc = pc.add(1);
3652 }
3653
3654 let call_ra = base.vm_reg_cursor(
3655 thread.stack_top(),
3656 call_instruction.a() as usize,
3657 );
3658 debug_assert!(call_ra == ra);
3659
3660 let n_params = i32::from(call_instruction.b()) - 1;
3661 let n_results = i32::from(call_instruction.c()) - 1;
3662
3663 thread.set_vm_saved_pc(pc);
3664 {
3665 thread.set_name_call(Some(kv.string_value()));
3666 let top = thread.stack_top();
3667 thread.set_stack_top(if n_params == LUA_MULTRET {
3668 top
3669 } else {
3670 ra.add(1 + n_params as usize)
3671 });
3672 }
3673
3674 thread.setup_cci(n_results, ra)?;
3675
3676 let mut cached_slot = (aux >> 16) as u16;
3677 debug_assert!({
3678 kv.string_value().as_ptr().as_ref().unwrap_unchecked().atom >= 0
3679 });
3680 let namecall_result = {
3681 function(
3682 thread,
3683 udata.cast(),
3684 i32::from(
3685 kv.string_value()
3686 .as_ptr()
3687 .as_ref()
3688 .unwrap_unchecked()
3689 .atom,
3690 ),
3691 &mut cached_slot,
3692 utag as i32,
3693 )
3694 };
3695
3696 if matches!(namecall_result, Ok(_) | Err(VmExit::Control(_)))
3697 && cached_slot != (aux >> 16) as u16
3698 && decode_instruction_opcode(*insn_pc) == Opcode::NameCallUData
3699 {
3700 patch_aux_slot(
3701 namecall_slot as *mut Instruction,
3702 kidx as u16,
3703 cached_slot,
3704 );
3705 }
3706 let results = namecall_result?;
3707
3708 let call_info = thread.current_call_info();
3709 let parent = thread.current_call_info_cursor().sub(1);
3710
3711 let mut result_slot = { call_info.function() };
3712 let mut value_iter = { thread.stack_top().sub(results) };
3713 let value_end = { thread.stack_top() };
3714 let mut remaining = n_results;
3715
3716 while remaining != 0 && value_iter < value_end {
3717 result_slot
3718 .value_unchecked()
3719 .set_obj(value_iter.value_unchecked());
3720 result_slot = result_slot.add(1);
3721 value_iter = value_iter.add(1);
3722 remaining -= 1;
3723 }
3724 while remaining > 0 {
3725 result_slot.value_unchecked().set_nil();
3726 result_slot = result_slot.add(1);
3727 remaining -= 1;
3728 }
3729
3730 {
3731 let parent_call_info = parent.call_info_unchecked();
3732 thread.set_current_call_info(parent);
3733 thread.set_stack_base(parent_call_info.base());
3734 thread.set_stack_top(if n_results == LUA_MULTRET {
3735 result_slot
3736 } else {
3737 parent_call_info.top()
3738 });
3739 }
3740 base = thread.stack_base();
3741 break 'dispatch;
3742 }
3743 }
3744
3745 patch_instruction_opcode(insn_pc as *mut Instruction, Opcode::NameCall);
3746 patch_aux_slot(namecall_slot as *mut Instruction, kidx as u16, 0);
3747 pc = insn_pc.add(1);
3748 dispatch_opcode = Opcode::NameCall;
3749 continue 'dispatch;
3750 }
3751 Opcode::JumpX => {
3752 thread.interrupt_vm(insn_pc)?;
3753 base = thread.stack_base();
3754 if flags::LuauBackedgeHeapCheck.get() && thread.needs_gc() {
3755 thread.set_vm_saved_pc(pc);
3756 thread.step(true)?;
3757 base = thread.stack_base();
3758 }
3759
3760 pc = pc.offset(instruction.e() as isize);
3761 proto.assert_vm_pc(pc);
3762 }
3763 Opcode::FastCall => {
3764 let builtin_id = instruction.a();
3765 let skip = instruction.c() as usize;
3766 debug_assert!(
3767 {
3768 pc.offset_from(proto.as_ptr().as_ref().unwrap_unchecked().code)
3769 as usize
3770 } + skip
3771 < { proto.as_ptr().as_ref().unwrap_unchecked().size_code as usize }
3772 );
3773
3774 let call = { *pc.add(skip) };
3775 debug_assert_eq!(call.opcode(), Opcode::Call);
3776
3777 let ra = base.vm_reg_cursor(thread.stack_top(), call.a() as usize);
3778 let mut nparams = i32::from(call.b()) - 1;
3779 let nresults = i32::from(call.c()) - 1;
3780
3781 if nparams == LUA_MULTRET {
3782 nparams = thread.stack_top().offset_from(ra) as i32 - 1;
3783 }
3784
3785 let builtin = crate::builtins::LUAU_F_TABLE[builtin_id as usize];
3786 let env = closure.env();
3787
3788 if env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 {
3789 thread.set_vm_saved_pc(pc);
3790 let produced = {
3791 builtin(
3792 thread,
3793 ra,
3794 ra.add(1).value_unchecked(),
3795 nresults,
3796 ra.add(2),
3797 nparams,
3798 )
3799 };
3800
3801 if produced >= 0 {
3802 {
3803 thread.set_stack_top(if nresults == LUA_MULTRET {
3804 ra.add(produced as usize)
3805 } else {
3806 thread.current_call_info().top()
3807 });
3808 }
3809
3810 pc = pc.add(skip + 1);
3811 proto.assert_vm_pc(pc);
3812 }
3813 }
3814 }
3815 Opcode::Coverage => {
3816 let hits = instruction.e();
3817 let patched_hits = if hits < (1 << 23) - 1 { hits + 1 } else { hits };
3818 let patched = Instruction::ae(Opcode::Coverage, patched_hits);
3819 {
3820 *(insn_pc as *mut Instruction) = patched;
3821 }
3822 }
3823 Opcode::Capture => {
3824 unreachable!(
3825 "CAPTURE is a pseudo-opcode and must execute as part of closure creation"
3826 );
3827 }
3828 Opcode::FastCall1 => {
3829 let builtin_id = instruction.a();
3830 let arg = base
3831 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3832 .value_unchecked();
3833 let skip = instruction.c() as usize;
3834 debug_assert!(
3835 {
3836 pc.offset_from(proto.as_ptr().as_ref().unwrap_unchecked().code)
3837 as usize
3838 } + skip
3839 < { proto.as_ptr().as_ref().unwrap_unchecked().size_code as usize }
3840 );
3841
3842 let call = { *pc.add(skip) };
3843 debug_assert_eq!(call.opcode(), Opcode::Call);
3844
3845 let ra = base.vm_reg_cursor(thread.stack_top(), call.a() as usize);
3846 let _ra_value = ra.value_unchecked();
3847 let nresults = i32::from(call.c()) - 1;
3848 let builtin = crate::builtins::LUAU_F_TABLE[builtin_id as usize];
3849 let env = closure.env();
3850
3851 if env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 {
3852 thread.set_vm_saved_pc(pc);
3853 let produced = { builtin(thread, ra, arg, nresults, ra.add(1), 1) };
3854
3855 if produced >= 0 {
3856 if nresults == LUA_MULTRET {
3857 {
3858 thread.set_stack_top(ra.add(produced as usize));
3859 }
3860 }
3861
3862 pc = pc.add(skip + 1);
3863 proto.assert_vm_pc(pc);
3864 }
3865 }
3866 }
3867 Opcode::FastCall2 => {
3868 let skip = instruction.c() as usize - 1;
3869 let aux = { InstructionAux::new((*pc).word()) };
3870 pc = pc.add(1);
3871
3872 let builtin_id = instruction.a();
3873 let arg1 = base
3874 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3875 .value_unchecked();
3876 let arg2 = base.vm_reg_cursor(thread.stack_top(), aux.word() as usize);
3877
3878 debug_assert!(
3879 {
3880 pc.offset_from(proto.as_ptr().as_ref().unwrap_unchecked().code)
3881 as usize
3882 } + skip
3883 < { proto.as_ptr().as_ref().unwrap_unchecked().size_code as usize }
3884 );
3885
3886 let call = { *pc.add(skip) };
3887 debug_assert_eq!(call.opcode(), Opcode::Call);
3888
3889 let ra = base.vm_reg_cursor(thread.stack_top(), call.a() as usize);
3890 let nresults = i32::from(call.c()) - 1;
3891 let builtin = crate::builtins::LUAU_F_TABLE[builtin_id as usize];
3892 let env = closure.env();
3893
3894 if env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 {
3895 thread.set_vm_saved_pc(pc);
3896 let produced = { builtin(thread, ra, arg1, nresults, arg2, 2) };
3897
3898 if produced >= 0 {
3899 if nresults == LUA_MULTRET {
3900 {
3901 thread.set_stack_top(ra.add(produced as usize));
3902 }
3903 }
3904
3905 pc = pc.add(skip + 1);
3906 proto.assert_vm_pc(pc);
3907 }
3908 }
3909 }
3910 Opcode::FastCall2K => {
3911 let skip = instruction.c() as usize - 1;
3912 let aux = { InstructionAux::new((*pc).word()) };
3913 pc = pc.add(1);
3914
3915 let builtin_id = instruction.a();
3916 let arg1 = base
3917 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3918 .value_unchecked();
3919 let arg2 = constants.add(aux.word() as usize);
3920
3921 debug_assert!(
3922 {
3923 pc.offset_from(proto.as_ptr().as_ref().unwrap_unchecked().code)
3924 as usize
3925 } + skip
3926 < { proto.as_ptr().as_ref().unwrap_unchecked().size_code as usize }
3927 );
3928
3929 let call = { *pc.add(skip) };
3930 debug_assert_eq!(call.opcode(), Opcode::Call);
3931
3932 let ra = base.vm_reg_cursor(thread.stack_top(), call.a() as usize);
3933 let nresults = i32::from(call.c()) - 1;
3934 let builtin = crate::builtins::LUAU_F_TABLE[builtin_id as usize];
3935 let env = closure.env();
3936
3937 if env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 {
3938 thread.set_vm_saved_pc(pc);
3939 let produced = { builtin(thread, ra, arg1, nresults, arg2, 2) };
3940
3941 if produced >= 0 {
3942 if nresults == LUA_MULTRET {
3943 {
3944 thread.set_stack_top(ra.add(produced as usize));
3945 }
3946 }
3947
3948 pc = pc.add(skip + 1);
3949 proto.assert_vm_pc(pc);
3950 }
3951 }
3952 }
3953 Opcode::FastCall3 => {
3954 let skip = instruction.c() as usize - 1;
3955 let aux = { InstructionAux::new((*pc).word()) };
3956 pc = pc.add(1);
3957
3958 let builtin_id = instruction.a();
3959 let arg1 = base
3960 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
3961 .value_unchecked();
3962 let arg2 = base
3963 .vm_reg_cursor(thread.stack_top(), aux.a() as usize)
3964 .value_unchecked();
3965 let arg3 = base
3966 .vm_reg_cursor(thread.stack_top(), aux.b() as usize)
3967 .value_unchecked();
3968
3969 debug_assert!(
3970 {
3971 pc.offset_from(proto.as_ptr().as_ref().unwrap_unchecked().code)
3972 as usize
3973 } + skip
3974 < { proto.as_ptr().as_ref().unwrap_unchecked().size_code as usize }
3975 );
3976
3977 let call = { *pc.add(skip) };
3978 debug_assert_eq!(call.opcode(), Opcode::Call);
3979
3980 let ra = base.vm_reg_cursor(thread.stack_top(), call.a() as usize);
3981 let nresults = i32::from(call.c()) - 1;
3982 let builtin = crate::builtins::LUAU_F_TABLE[builtin_id as usize];
3983 let env = closure.env();
3984
3985 if env.as_ptr().as_ref().unwrap_unchecked().safe_env != 0 {
3986 thread.set_vm_saved_pc(pc);
3987 debug_assert!(
3988 thread.stack_top().add(2)
3989 < thread.stack().add(
3990 thread.as_ptr().as_ref().unwrap_unchecked().stack_size
3991 as usize
3992 )
3993 );
3994 let top_value = thread.stack_top();
3995 {
3996 top_value.value_unchecked().set_obj(arg2);
3997 top_value.add(1).value_unchecked().set_obj(arg3);
3998 }
3999
4000 let produced = { builtin(thread, ra, arg1, nresults, top_value, 3) };
4001
4002 if produced >= 0 {
4003 if nresults == LUA_MULTRET {
4004 {
4005 thread.set_stack_top(ra.add(produced as usize));
4006 }
4007 }
4008
4009 pc = pc.add(skip + 1);
4010 proto.assert_vm_pc(pc);
4011 }
4012 }
4013 }
4014 Opcode::Call => {
4015 thread.interrupt_vm(insn_pc)?;
4016 base = thread.stack_base();
4017
4018 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
4019 let ra_value = ra.value_unchecked();
4020 let nparams = i32::from(instruction.b()) - 1;
4021 let nresults = i32::from(instruction.c()) - 1;
4022
4023 let mut arg_top = { thread.stack_top() };
4024 if nparams != LUA_MULTRET {
4025 arg_top = ra.add(1 + nparams as usize);
4026 }
4027
4028 if !ra_value.is_function() {
4029 thread.set_vm_saved_pc(pc);
4030 thread.try_func_tm(ra)?;
4031 arg_top = arg_top.add(1);
4032 }
4033
4034 let called_closure = { ra.value_unchecked().closure_value() };
4035 thread.set_vm_saved_pc(pc);
4036
4037 let call_info = thread.incr_ci()?.call_info_unchecked();
4038 {
4039 call_info.init_call(
4040 ra,
4041 arg_top.add(
4042 called_closure
4043 .as_ptr()
4044 .as_ref()
4045 .unwrap_unchecked()
4046 .stack_size as usize,
4047 ),
4048 nresults,
4049 called_closure.proto(),
4050 );
4051 }
4052
4053 {
4054 thread.set_stack_base(call_info.base());
4055 thread.set_stack_top(arg_top);
4056 }
4057
4058 thread.check_stack_for_new_ci(
4059 called_closure
4060 .as_ptr()
4061 .as_ref()
4062 .unwrap_unchecked()
4063 .stack_size as i32,
4064 )?;
4065 debug_assert!(call_info.top() <= thread.stack_last());
4066
4067 if called_closure.is_lua() {
4068 let called_proto = { called_closure.proto().unwrap_unchecked() };
4069
4070 {
4071 let mut arg_cursor = thread.stack_top();
4072 let arg_end_cursor = thread.stack_base().add(
4073 called_proto.as_ptr().as_ref().unwrap_unchecked().num_params
4074 as usize,
4075 );
4076 while arg_cursor < arg_end_cursor {
4077 arg_cursor.value_unchecked().set_nil();
4078 arg_cursor = arg_cursor.add(1);
4079 }
4080
4081 thread.set_stack_top(
4082 if called_proto.as_ptr().as_ref().unwrap_unchecked().is_vararg
4083 != 0
4084 {
4085 arg_cursor
4086 } else {
4087 call_info.top()
4088 },
4089 );
4090
4091 call_info.set_saved_pc(
4092 called_proto
4093 .as_ptr()
4094 .as_ref()
4095 .unwrap_unchecked()
4096 .code
4097 .cast_const(),
4098 );
4099
4100 if called_proto
4101 .as_ptr()
4102 .as_ref()
4103 .unwrap_unchecked()
4104 .exec_target
4105 != 0
4106 && !called_proto
4107 .as_ptr()
4108 .as_ref()
4109 .unwrap_unchecked()
4110 .exec_data
4111 .is_null()
4112 {
4113 call_info.as_ptr().as_mut().unwrap_unchecked().flags =
4114 LUA_CALLINFO_NATIVE;
4115 }
4116 }
4117
4118 pc = if thread.as_ptr().as_ref().unwrap_unchecked().single_step {
4119 called_proto
4120 .as_ptr()
4121 .as_ref()
4122 .unwrap_unchecked()
4123 .code
4124 .cast_const()
4125 } else {
4126 called_proto.as_ptr().as_ref().unwrap_unchecked().code_entry
4127 };
4128 closure = called_closure;
4129 base = thread.stack_base();
4130 proto = called_proto;
4131 constants = called_proto.constants();
4132 if pc == {
4133 called_proto.as_ptr().as_ref().unwrap_unchecked().code_entry
4134 } {
4135 called_proto.assert_vm_pc(
4136 called_proto
4137 .as_ptr()
4138 .as_ref()
4139 .unwrap_unchecked()
4140 .code
4141 .cast_const(),
4142 );
4143 } else {
4144 called_proto.assert_vm_pc(pc);
4145 }
4146 } else {
4147 thread.finish_native_call(called_closure, nresults)?;
4148
4149 base = thread.stack_base();
4150 }
4151 }
4152 Opcode::CallFb => {
4153 debug_assert!(flags::LuauCallFeedback.get());
4154 thread.interrupt_vm(insn_pc)?;
4155 base = thread.stack_base();
4156
4157 let feedback_slot = { (*pc).word() };
4158 pc = pc.add(1);
4159
4160 let ra = base.vm_reg_cursor(thread.stack_top(), instruction.a() as usize);
4161 let ra_value = ra.value_unchecked();
4162 let nparams = i32::from(instruction.b()) - 1;
4163 let nresults = i32::from(instruction.c()) - 1;
4164
4165 let mut arg_top = { thread.stack_top() };
4166 if nparams != LUA_MULTRET {
4167 arg_top = ra.add(1 + nparams as usize);
4168 }
4169
4170 if !ra_value.is_function() {
4171 if feedback_slot != u32::MAX {
4172 {
4173 *(pc.sub(1) as *mut Instruction) = Instruction::new(u32::MAX);
4174 }
4175 }
4176
4177 thread.set_vm_saved_pc(pc);
4178 thread.try_func_tm(ra)?;
4179 arg_top = arg_top.add(1);
4180 }
4181
4182 let called_closure = { ra.value_unchecked().closure_value() };
4183 thread.set_vm_saved_pc(pc);
4184
4185 let call_info = thread.incr_ci()?.call_info_unchecked();
4186 {
4187 call_info.init_call(
4188 ra,
4189 arg_top.add(
4190 called_closure
4191 .as_ptr()
4192 .as_ref()
4193 .unwrap_unchecked()
4194 .stack_size as usize,
4195 ),
4196 nresults,
4197 called_closure.proto(),
4198 );
4199 }
4200
4201 {
4202 thread.set_stack_base(call_info.base());
4203 thread.set_stack_top(arg_top);
4204 }
4205
4206 thread.check_stack_for_new_ci(
4207 called_closure
4208 .as_ptr()
4209 .as_ref()
4210 .unwrap_unchecked()
4211 .stack_size as i32,
4212 )?;
4213 debug_assert!(call_info.top() <= thread.stack_last());
4214
4215 if called_closure.is_lua() {
4216 let called_proto = { called_closure.proto().unwrap_unchecked() };
4217
4218 if feedback_slot != u32::MAX
4219 && !thread.record_hit(closure, called_closure, feedback_slot)
4220 {
4221 {
4222 *(pc.sub(1) as *mut Instruction) = Instruction::new(u32::MAX);
4223 }
4224 }
4225
4226 {
4227 let mut arg_cursor = thread.stack_top();
4228 let arg_end_cursor = thread.stack_base().add(
4229 called_proto.as_ptr().as_ref().unwrap_unchecked().num_params
4230 as usize,
4231 );
4232 while arg_cursor < arg_end_cursor {
4233 arg_cursor.value_unchecked().set_nil();
4234 arg_cursor = arg_cursor.add(1);
4235 }
4236
4237 thread.set_stack_top(
4238 if called_proto.as_ptr().as_ref().unwrap_unchecked().is_vararg
4239 != 0
4240 {
4241 arg_cursor
4242 } else {
4243 call_info.top()
4244 },
4245 );
4246
4247 call_info.set_saved_pc(
4248 called_proto
4249 .as_ptr()
4250 .as_ref()
4251 .unwrap_unchecked()
4252 .code
4253 .cast_const(),
4254 );
4255
4256 if called_proto
4257 .as_ptr()
4258 .as_ref()
4259 .unwrap_unchecked()
4260 .exec_target
4261 != 0
4262 && !called_proto
4263 .as_ptr()
4264 .as_ref()
4265 .unwrap_unchecked()
4266 .exec_data
4267 .is_null()
4268 {
4269 call_info.as_ptr().as_mut().unwrap_unchecked().flags =
4270 LUA_CALLINFO_NATIVE;
4271 }
4272 }
4273
4274 pc = if thread.as_ptr().as_ref().unwrap_unchecked().single_step {
4275 called_proto
4276 .as_ptr()
4277 .as_ref()
4278 .unwrap_unchecked()
4279 .code
4280 .cast_const()
4281 } else {
4282 called_proto.as_ptr().as_ref().unwrap_unchecked().code_entry
4283 };
4284 closure = called_closure;
4285 base = thread.stack_base();
4286 proto = called_proto;
4287 constants = called_proto.constants();
4288 if pc == {
4289 called_proto.as_ptr().as_ref().unwrap_unchecked().code_entry
4290 } {
4291 called_proto.assert_vm_pc(
4292 called_proto
4293 .as_ptr()
4294 .as_ref()
4295 .unwrap_unchecked()
4296 .code
4297 .cast_const(),
4298 );
4299 } else {
4300 called_proto.assert_vm_pc(pc);
4301 }
4302 } else {
4303 if feedback_slot != u32::MAX {
4304 {
4305 *(pc.sub(1) as *mut Instruction) = Instruction::new(u32::MAX);
4306 }
4307 }
4308
4309 thread.finish_native_call(called_closure, nresults)?;
4310
4311 base = thread.stack_base();
4312 }
4313 }
4314 Opcode::Return => {
4315 thread.interrupt_vm(insn_pc)?;
4316 base = thread.stack_base();
4317
4318 let ra = { base.add(instruction.a() as usize) };
4319 let b = i32::from(instruction.b()) - 1;
4320
4321 let call_info = { thread.current_call_info() };
4322 let parent_cursor = thread.current_call_info_cursor().sub(1);
4323 let parent = parent_cursor.call_info_unchecked();
4324
4325 {
4326 let mut result = call_info.function();
4327 let mut value = ra;
4328 let value_end = if b == LUA_MULTRET {
4329 thread.stack_top()
4330 } else {
4331 ra.add(b as usize)
4332 };
4333 let mut remaining =
4334 call_info.as_ptr().as_ref().unwrap_unchecked().n_results;
4335
4336 while remaining != 0 && value < value_end {
4337 result.value_unchecked().set_obj(value.value_unchecked());
4338 result = result.add(1);
4339 value = value.add(1);
4340 remaining -= 1;
4341 }
4342
4343 while remaining > 0 {
4344 result.value_unchecked().set_nil();
4345 result = result.add(1);
4346 remaining -= 1;
4347 }
4348
4349 thread.set_current_call_info(parent_cursor);
4350 thread.set_stack_base(parent.base());
4351 thread.set_stack_top(
4352 if call_info.as_ptr().as_ref().unwrap_unchecked().n_results
4353 == LUA_MULTRET
4354 {
4355 result
4356 } else {
4357 parent.top()
4358 },
4359 );
4360 }
4361
4362 if {
4363 call_info.as_ptr().as_ref().unwrap_unchecked().flags
4364 & crate::state::LUA_CALLINFO_RETURN
4365 } != 0
4366 {
4367 return Ok(());
4368 }
4369
4370 let next_closure = { parent.function_closure() };
4371 let next_proto = { next_closure.proto().unwrap_unchecked() };
4372
4373 if {
4374 parent.as_ptr().as_ref().unwrap_unchecked().flags & LUA_CALLINFO_NATIVE
4375 } != 0
4376 && !thread.as_ptr().as_ref().unwrap_unchecked().single_step
4377 {
4378 let Some(enter) = global.execution_enter() else {
4379 unreachable!(
4380 "native lvmexecute requires an execution enter callback"
4381 );
4382 };
4383
4384 if { enter(thread, next_proto) } == 0 {
4385 return Ok(());
4386 }
4387
4388 let resumed_call = thread.current_call_info();
4389 closure = resumed_call.function_closure();
4390 proto = closure.proto().unwrap_unchecked();
4391 pc = resumed_call.saved_pc();
4392 base = thread.stack_base();
4393 constants = proto.constants();
4394 proto.assert_vm_pc(pc);
4395 } else {
4396 pc = parent.saved_pc();
4397 closure = next_closure;
4398 base = thread.stack_base();
4399 proto = next_proto;
4400 constants = proto.constants();
4401 proto.assert_vm_pc(pc);
4402 }
4403 }
4404 Opcode::JumpXEqKNil => {
4405 let aux = { InstructionAux::new((*pc).word()) };
4406 let ra = base
4407 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4408 .value_unchecked();
4409 let condition = { ra.is_nil() } != aux.is_negated();
4410 pc = if condition {
4411 pc.offset(i32::from(instruction.d()) as isize)
4412 } else {
4413 pc.add(1)
4414 };
4415 proto.assert_vm_pc(pc);
4416 }
4417 Opcode::JumpXEqKB => {
4418 let aux = { InstructionAux::new((*pc).word()) };
4419 let ra = base
4420 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4421 .value_unchecked();
4422 let condition =
4423 { ra.is_boolean() && ra.boolean_value() == i32::from(aux.kb()) }
4424 != aux.is_negated();
4425 pc = if condition {
4426 pc.offset(i32::from(instruction.d()) as isize)
4427 } else {
4428 pc.add(1)
4429 };
4430 proto.assert_vm_pc(pc);
4431 }
4432 Opcode::JumpXEqKN => {
4433 let aux = { InstructionAux::new((*pc).word()) };
4434 let ra = base
4435 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4436 .value_unchecked();
4437 let kv = proto.vm_const(constants, aux.kv() as usize);
4438 #[cfg(target_arch = "aarch64")]
4439 {
4440 if aux.is_negated() {
4441 pc = if !ra.is_number() || ra.number_value() != kv.number_value() {
4442 pc.offset(i32::from(instruction.d()) as isize)
4443 } else {
4444 pc.add(1)
4445 };
4446 } else {
4447 pc = if ra.is_number() && ra.number_value() == kv.number_value() {
4448 pc.offset(i32::from(instruction.d()) as isize)
4449 } else {
4450 pc.add(1)
4451 };
4452 }
4453 }
4454 #[cfg(not(target_arch = "aarch64"))]
4455 {
4456 let condition = (ra.is_number()
4457 && ra.number_value() == kv.number_value())
4458 != aux.is_negated();
4459 pc = if condition {
4460 pc.offset(i32::from(instruction.d()) as isize)
4461 } else {
4462 pc.add(1)
4463 };
4464 }
4465 proto.assert_vm_pc(pc);
4466 }
4467 Opcode::JumpXEqKS => {
4468 let aux = { InstructionAux::new((*pc).word()) };
4469 let ra = base
4470 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4471 .value_unchecked();
4472 let kv = proto.vm_const(constants, aux.kv() as usize);
4473 debug_assert!({ kv.is_string() });
4474 let condition = { ra.is_string() && ra.gc_value() == kv.gc_value() }
4475 != aux.is_negated();
4476 pc = if condition {
4477 pc.offset(i32::from(instruction.d()) as isize)
4478 } else {
4479 pc.add(1)
4480 };
4481 proto.assert_vm_pc(pc);
4482 }
4483 Opcode::NewClassMember => {
4484 let aux = { (*pc).word() };
4485 pc = pc.add(1);
4486
4487 let ra = base
4488 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4489 .value_unchecked();
4490 let member_name = proto.vm_const(constants, aux as usize);
4491 debug_assert!({ member_name.is_string() });
4492 debug_assert_eq!(instruction.b(), 0);
4493 let rc = base
4494 .vm_reg_cursor(thread.stack_top(), instruction.c() as usize)
4495 .value_unchecked();
4496 thread.set_vm_saved_pc(pc);
4497 { ra.class_value() }.add_class_member(
4498 thread,
4499 member_name.string_value(),
4500 rc,
4501 )?;
4502 }
4503 Opcode::NewClass => {
4504 let aux = (*pc).word();
4505 pc = pc.add(1);
4506
4507 let ra = base
4508 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4509 .value_unchecked();
4510 let class_shape = proto.vm_const(constants, aux as usize);
4511 debug_assert!(class_shape.is_class());
4512 ra.set_obj(class_shape);
4513
4514 if instruction.b() != u8::MAX {
4515 thread.set_vm_saved_pc(pc);
4516
4517 let superclass = base
4518 .vm_reg_cursor(thread.stack_top(), instruction.b() as usize)
4519 .value_unchecked();
4520 if !superclass.is_class() {
4521 return thread.type_error(superclass, "extend").map_err(Into::into);
4522 }
4523
4524 let class = thread.inherit_class(
4525 class_shape.class_value(),
4526 superclass.class_value(),
4527 )?;
4528 ra.set_class_value(class);
4529 }
4530 }
4531 Opcode::CmpProto => {
4532 let fun_id = { (*pc).word() };
4533 let ra = base
4534 .vm_reg_cursor(thread.stack_top(), instruction.a() as usize)
4535 .value_unchecked();
4536
4537 let is_function = { ra.is_function() };
4538 let should_jump = if !is_function {
4539 true
4540 } else {
4541 let called = { ra.closure_value() };
4542 if called.is_native() {
4543 true
4544 } else {
4545 let called_proto = called.proto().unwrap_unchecked();
4546 called_proto.as_ptr().as_ref().unwrap_unchecked().fun_id != fun_id
4547 }
4548 };
4549
4550 pc = if should_jump {
4551 pc.offset(i32::from(instruction.d()) as isize)
4552 } else {
4553 pc.add(1)
4554 };
4555 proto.assert_vm_pc(pc);
4556 }
4557 }
4558
4559 break 'dispatch;
4560 }
4561 }
4562 }
4563}