Skip to main content

lua_types/
closure.rs

1//! `LuaClosure` — the function variant of `LuaValue`. Three sub-kinds:
2//! Lua closure (compiled Proto + upvalues), C closure (function pointer +
3//! upvalues), light C function (function pointer, no upvalues).
4
5use std::cell::{Cell, RefCell};
6
7use crate::gc::GcRef;
8use crate::proto::LuaProto;
9use crate::upval::UpVal;
10use crate::value::LuaValue;
11
12/// Opaque registry index into `GlobalState.c_functions`, where the real
13/// `lua_CFunction` (`fn(&mut LuaState) -> Result<usize, LuaError>`) is stored.
14/// Lua-types can't reference `LuaState` without a circular dep, so we keep
15/// the closure variant type-erased here and resolve through the registry at
16/// call time.
17pub type LuaCFnPtr = usize;
18
19#[derive(Debug, Clone, Copy)]
20pub enum LuaClosure {
21    Lua(GcRef<LuaLClosure>),
22    C(GcRef<LuaCClosure>),
23    LightC(LuaCFnPtr),
24}
25
26#[derive(Debug)]
27pub struct LuaLClosure {
28    pub proto: GcRef<LuaProto>,
29    /// Each upvalue slot is held in a `Cell` so that `debug.upvaluejoin`
30    /// can replace an entry with another closure's slot without rebuilding
31    /// the (shared) closure. `GcRef<UpVal>` is `Copy` (thin wrapper over
32    /// `Gc<UpVal>`), so a plain `Cell` is sufficient and skips RefCell
33    /// borrow tracking on every upvalue read — critical for the
34    /// `upvalue_get` hot path.
35    pub upvals: Vec<Cell<GcRef<UpVal>>>,
36}
37
38#[derive(Debug)]
39pub struct LuaCClosure {
40    pub func: LuaCFnPtr,
41    pub upvalues: RefCell<Vec<LuaValue>>,
42}
43
44impl LuaLClosure {
45    pub fn placeholder() -> Self {
46        LuaLClosure {
47            proto: GcRef::new(LuaProto::placeholder()),
48            upvals: Vec::new(),
49        }
50    }
51
52    /// Returns the upvalue slot at index `i`. Cheap (Copy of a one-pointer
53    /// `GcRef<UpVal>`).
54    #[inline(always)]
55    pub fn upval(&self, i: usize) -> GcRef<UpVal> {
56        self.upvals[i].get()
57    }
58
59    /// Replaces the upvalue slot at index `i` with `new`. Used by
60    /// `debug.upvaluejoin` to share an upvalue between two closures.
61    pub fn set_upval(&self, i: usize, new: GcRef<UpVal>) {
62        self.upvals[i].set(new);
63    }
64
65    /// Bytes owned outside the `GcBox` header/object allocation.
66    pub fn buffer_bytes(&self) -> usize {
67        self.upvals.capacity() * std::mem::size_of::<Cell<GcRef<UpVal>>>()
68    }
69}
70
71impl LuaCClosure {
72    /// Bytes owned outside the `GcBox` header/object allocation.
73    pub fn buffer_bytes(&self) -> usize {
74        self.upvalues.borrow().capacity() * std::mem::size_of::<LuaValue>()
75    }
76}
77
78// ──────────────────────────────────────────────────────────────────────────────
79// PORT STATUS
80//   source:        src/lobject.h (CClosure / LClosure / Closure union)
81//   target_crate:  lua-types
82//   confidence:    high
83//   todos:         0
84//   port_notes:    0
85//   unsafe_blocks: 0
86//   notes:         LuaClosure enum covering the C-Lua C/LightC/Lua closure variants.
87//                  C uses a union with a common header; we use a tagged enum.
88//                  LuaLClosure.upvals uses Cell<GcRef<UpVal>> (not RefCell) so per-
89//                  upvalue reads avoid borrow-tracking; GcRef<UpVal> is Copy.
90// ──────────────────────────────────────────────────────────────────────────────