Skip to main content

luna_core/vm/
table_builder.rs

1//! `TableBuilder` + `vm.table_of` (B3, Phase 2 P2-B) โ€” replace the
2//! dogfood ยง4.1 `unsafe { t.as_mut() }.set(...)` dance with a safe
3//! one-line builder.
4//!
5//! ```
6//! use luna_core::vm::Vm;
7//! use luna_core::version::LuaVersion;
8//! use luna_core::runtime::Value;
9//!
10//! let mut vm = Vm::sandbox(LuaVersion::Lua55).open_base().build();
11//!
12//! // One-shot: table_of
13//! let t = vm.table_of([("answer", 42_i64), ("year", 2026)]);
14//! vm.set_global("constants", Value::Table(t)).unwrap();
15//!
16//! // Multi-step: new_table builder
17//! let t = vm.new_table()
18//!     .with("name", "luna")
19//!     .with("major", 1_i64)
20//!     .with("minor", 1_i64)
21//!     .build();
22//! vm.set_global("info", Value::Table(t)).unwrap();
23//!
24//! let r = vm.eval("return constants.answer + info.minor").unwrap();
25//! assert_eq!(r.len(), 1);
26//! ```
27//!
28//! The unsafe `Gc::as_mut` lives inside the builder; embedders never
29//! write it.
30
31use crate::runtime::Table;
32use crate::runtime::heap::Gc;
33use crate::vm::error::LuaError;
34use crate::vm::exec::Vm;
35use crate::vm::into_value::IntoValue;
36
37/// Multi-step table construction. Borrows `&mut Vm` for the whole
38/// builder window so no other Vm operation can interleave (which
39/// might trigger GC mid-build). Consume with [`TableBuilder::build`].
40pub struct TableBuilder<'vm> {
41    vm: &'vm mut Vm,
42    t: Gc<Table>,
43}
44
45impl<'vm> TableBuilder<'vm> {
46    /// Add a `(key, value)` entry. Both may be any [`IntoValue`].
47    /// Panics if the table overflows (`MAX_ASIZE = 1<<27`; unreachable
48    /// in practice โ€” embedders building tables that large have other
49    /// problems).
50    pub fn with<K, V>(self, k: K, v: V) -> Self
51    where
52        K: IntoValue,
53        V: IntoValue,
54    {
55        let TableBuilder { vm, t } = self;
56        let k = k.into_value(vm);
57        let v = v.into_value(vm);
58        // SAFETY: Gc<T> is NonNull<T> over the single-threaded GC heap
59        // (see heap.rs:5-7); the TableBuilder's exclusive &mut Vm borrow
60        // guarantees no concurrent access to the table.
61        unsafe { t.as_mut() }
62            .set(&mut vm.heap, k, v)
63            .expect("table builder overflow");
64        TableBuilder { vm, t }
65    }
66
67    /// Fallible variant of [`TableBuilder::with`] โ€” propagates
68    /// `TableError::Overflow` as `LuaError` instead of panicking.
69    pub fn try_with<K, V>(self, k: K, v: V) -> Result<Self, LuaError>
70    where
71        K: IntoValue,
72        V: IntoValue,
73    {
74        let TableBuilder { vm, t } = self;
75        let k = k.into_value(vm);
76        let v = v.into_value(vm);
77        // SAFETY: same as with().
78        unsafe { t.as_mut() }.set(&mut vm.heap, k, v)?;
79        Ok(TableBuilder { vm, t })
80    }
81
82    /// Finalize: emit a GC write barrier (so any newly-rooted children
83    /// are visible to the collector) and return the table handle.
84    pub fn build(self) -> Gc<Table> {
85        let TableBuilder { vm, t } = self;
86        vm.heap
87            .barrier_back(t.as_ptr() as *mut crate::runtime::heap::GcHeader);
88        t
89    }
90}
91
92impl Vm {
93    /// Allocate a fresh table and return a builder for in-place population.
94    pub fn new_table(&mut self) -> TableBuilder<'_> {
95        let t = self.heap.new_table();
96        TableBuilder { vm: self, t }
97    }
98
99    /// Allocate a fresh table populated from a fixed-size slice of
100    /// `(key, value)` pairs. Equivalent to chained `new_table().with(...)`
101    /// calls, but more concise for static tables (stdlib registration,
102    /// embedder-side constants).
103    ///
104    /// Panics on table overflow (unreachable for `N` small).
105    pub fn table_of<K, V, const N: usize>(&mut self, entries: [(K, V); N]) -> Gc<Table>
106    where
107        K: IntoValue,
108        V: IntoValue,
109    {
110        let mut b = self.new_table();
111        for (k, v) in entries {
112            b = b.with(k, v);
113        }
114        b.build()
115    }
116}