luna-jit 1.1.0

A Lua runtime in pure Rust — full 5.1/5.2/5.3/5.4/5.5 support. Equivalent to `luna-core` plus a Cranelift-backed JIT (method + trace) and the lua.h-compatible C ABI. (The `luna` crate name on crates.io is taken by an unrelated utility library; this crate is the JIT-equipped variant of the goliajp/luna Lua runtime.)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
//! mlua-style `Lua` facade (B12, Phase 2 P2-D).
//!
//! A thin wrapper around [`luna_core::vm::Vm`] that exposes the same
//! API in a shape familiar to embedders coming from `rlua` / `mlua`:
//!
//! ```
//! use luna_jit::Lua;
//!
//! let mut lua = Lua::new();
//! lua.open_base();
//! lua.open_math();
//! let r: i64 = lua.eval("return 1 + 2").unwrap();
//! assert_eq!(r, 3);
//!
//! let add = lua.create_function(|a: i64, b: i64| -> i64 { a + b });
//! lua.set_global("add", add).unwrap();
//! let r: i64 = lua.eval("return add(40, 2)").unwrap();
//! assert_eq!(r, 42);
//! ```
//!
//! ## Handles
//!
//! [`LuaFunction`] / [`LuaTable`] / [`LuaRoot<V>`] are `Copy` wrappers
//! around an index into [`Vm::pin_host`]'s pool. They keep their
//! referenced `Gc<T>` alive across calls (so a `LuaTable` survives
//! a GC cycle even when no Lua-side reference exists). v1.1 ships
//! append-only pin semantics; release the entire pool with
//! [`Lua::unpin_all`]. Slot recycling lands in Phase 3 alongside B8.
//!
//! ## Threading
//!
//! `Lua` inherits `Vm`'s `!Send + !Sync` contract. See
//! [`docs/threading.md`](../../../../docs/threading.md) for canonical
//! embedding patterns.

use luna_core::runtime::Value;
use luna_core::version::LuaVersion;
use luna_core::vm::{FromLuaValue, IntoValue, LuaError, NativeTypedSig, SandboxBuilder, Vm};

/// `mlua`-style front door for embedders. Wraps a [`Vm`] with JIT
/// installed by default (`Vm::new_minimal_with_jit`).
pub struct Lua(Vm);

impl Lua {
    /// Create a Lua VM with JIT installed + Lua 5.5 dialect.
    pub fn new() -> Lua {
        Lua(crate::new_minimal_with_jit(LuaVersion::Lua55))
    }

    /// Pick a specific dialect (5.1-5.5).
    pub fn with_version(v: LuaVersion) -> Lua {
        Lua(crate::new_minimal_with_jit(v))
    }

    /// Sandbox-mode builder — same as [`Vm::sandbox`] but doesn't
    /// install JIT by default. `.build_lua()` finalizes to a `Lua`
    /// wrapping the sandboxed `Vm`.
    pub fn sandbox(v: LuaVersion) -> LuaSandboxBuilder {
        LuaSandboxBuilder {
            inner: Vm::sandbox(v),
        }
    }

    /// Borrow the underlying `Vm` for direct access (escape hatch
    /// for cases the facade doesn't cover).
    pub fn vm(&mut self) -> &mut Vm {
        &mut self.0
    }

    /// Open the base library (`print`, `type`, `pcall`, etc.).
    pub fn open_base(&mut self) {
        self.0.open_base();
    }

    /// Open the math library.
    pub fn open_math(&mut self) {
        self.0.open_math();
    }

    /// Open the string library.
    pub fn open_string(&mut self) {
        self.0.open_string();
    }

    /// Open the table library.
    pub fn open_table(&mut self) {
        self.0.open_table();
    }

    /// Open the coroutine library.
    pub fn open_coroutine(&mut self) {
        self.0.open_coroutine();
    }

    /// Compile and run `src`; extract the first return value as `T`.
    /// Use [`Lua::eval_multi`] to retrieve all returns.
    pub fn eval<T: FromLuaValue>(&mut self, src: &str) -> Result<T, LuaError> {
        let mut r = self.0.eval(src)?;
        if r.is_empty() {
            T::from_lua_value(Value::Nil)
        } else {
            T::from_lua_value(r.remove(0))
        }
    }

    /// Compile and run `src`; return all results.
    pub fn eval_multi(&mut self, src: &str) -> Result<Vec<Value>, LuaError> {
        self.0.eval(src)
    }

    /// Async variant of [`Lua::eval`]. Returns an `!Send` future that
    /// drives the dispatcher with cooperative yields on instruction
    /// budget exhaustion. Pin this to a `current_thread` Tokio
    /// runtime (or a `LocalSet` inside multi-thread Tokio) — see
    /// `docs/threading.md` and `examples/async_host.rs`.
    pub async fn eval_async<T: FromLuaValue>(&mut self, src: &str) -> Result<T, LuaError> {
        let mut r = self.0.eval_async(src).await?;
        if r.is_empty() {
            T::from_lua_value(Value::Nil)
        } else {
            T::from_lua_value(r.remove(0))
        }
    }

    /// Async variant of [`Lua::eval_multi`].
    pub async fn eval_async_multi(&mut self, src: &str) -> Result<Vec<Value>, LuaError> {
        self.0.eval_async(src).await
    }

    /// Register an async native function callable from Lua. The
    /// raw fn pointer ABI takes `(*mut Vm, func_slot, nargs)` and
    /// returns a boxed future — see [`luna_core::vm::AsyncNativeFn`]
    /// for the safety contract.
    ///
    /// Calling an async native from inside `vm.eval()` (sync mode)
    /// errors with a typed `LuaError`; embedders must drive the call
    /// through `eval_async`.
    pub fn set_async_native(
        &mut self,
        name: &str,
        f: luna_core::vm::AsyncNativeFn,
    ) -> Result<(), LuaError> {
        self.0.set_async_native(name, f)
    }

    /// Set a global by name. Accepts any [`IntoValue`] including
    /// `LuaFunction` / `LuaTable` / `LuaRoot` (the handle types impl
    /// `IntoValue` so they fan in alongside primitives + `Value`).
    pub fn set_global<V: IntoValue>(&mut self, name: &str, v: V) -> Result<(), LuaError> {
        self.0.set_global(name, v)
    }

    /// Borrow the globals table as a [`LuaTable`] handle.
    pub fn globals(&mut self) -> LuaTable {
        let g = self.0.globals();
        let idx = self.0.pin_host(Value::Table(g));
        LuaTable { idx }
    }

    /// Allocate a fresh empty table; return a handle that keeps it alive.
    pub fn create_table(&mut self) -> LuaTable {
        let t = self.0.new_table().build();
        let idx = self.0.pin_host(Value::Table(t));
        LuaTable { idx }
    }

    /// Wrap a typed Rust function as a Lua callable. See
    /// [`Vm::native_typed`] for the supported callable shapes.
    pub fn create_function<F, Marker>(&mut self, f: F) -> LuaFunction
    where
        F: NativeTypedSig<Marker>,
    {
        let v = self.0.native_typed(f);
        let idx = self.0.pin_host(v);
        LuaFunction { idx }
    }

    /// Pin an arbitrary value as a host root; the returned [`LuaRoot`]
    /// keeps it alive until [`Lua::unpin_all`].
    pub fn pin<V: IntoValue>(&mut self, v: V) -> LuaRoot {
        let v = v.into_value(&mut self.0);
        let idx = self.0.pin_host(v);
        LuaRoot { idx }
    }

    /// Drop every pinned handle. `LuaFunction` / `LuaTable` / `LuaRoot`
    /// created before this call become invalid (panic on use).
    pub fn unpin_all(&mut self) {
        self.0.unpin_all();
    }

    /// Number of currently-pinned handles (diagnostic).
    pub fn pinned_count(&self) -> usize {
        self.0.host_root_count()
    }
}

impl Default for Lua {
    fn default() -> Self {
        Lua::new()
    }
}

/// Sandbox builder that finalizes to a `Lua` (instead of a bare `Vm`).
pub struct LuaSandboxBuilder {
    inner: SandboxBuilder,
}

impl LuaSandboxBuilder {
    /// Whitelist the `base` standard library.
    pub fn open_base(mut self) -> Self {
        self.inner = self.inner.open_base();
        self
    }
    /// Whitelist the `math` standard library.
    pub fn open_math(mut self) -> Self {
        self.inner = self.inner.open_math();
        self
    }
    /// Whitelist the `string` standard library.
    pub fn open_string(mut self) -> Self {
        self.inner = self.inner.open_string();
        self
    }
    /// Whitelist the `table` standard library.
    pub fn open_table(mut self) -> Self {
        self.inner = self.inner.open_table();
        self
    }
    /// Whitelist the `coroutine` standard library.
    pub fn open_coroutine(mut self) -> Self {
        self.inner = self.inner.open_coroutine();
        self
    }
    /// Cap interpreter instruction count per call (fires once, then trips).
    pub fn with_instr_budget(mut self, n: i64) -> Self {
        self.inner = self.inner.with_instr_budget(n);
        self
    }
    /// Cap heap memory (approximate; see [`crate::vm::Vm::set_memory_cap`]).
    pub fn with_memory_cap(mut self, n: usize) -> Self {
        self.inner = self.inner.with_memory_cap(n);
        self
    }
    /// Re-enable precompiled-bytecode loading (off by default in sandbox
    /// mode for safety).
    pub fn allow_bytecode_loading(mut self) -> Self {
        self.inner = self.inner.allow_bytecode_loading();
        self
    }
    /// Finalize the builder and return a configured [`Lua`].
    pub fn build(self) -> Lua {
        Lua(self.inner.build())
    }
}

// ─────────────────────────────────────────────────────────────────────
// Handle types
// ─────────────────────────────────────────────────────────────────────

/// Handle to a Lua-callable value (`Value::Closure` or
/// `Value::Native`) pinned in the host root pool. `Copy`-able —
/// clones share the same pinned slot.
#[derive(Copy, Clone, Debug)]
pub struct LuaFunction {
    idx: usize,
}

impl LuaFunction {
    /// Call this function with the given typed args; decode the
    /// (first) return as `R`. Use [`LuaFunction::call_multi`] for
    /// the full result vector.
    pub fn call<A, R>(self, lua: &mut Lua, args: A) -> Result<R, LuaError>
    where
        A: IntoLuaArgs,
        R: FromLuaValue,
    {
        let f = lua.0.host_root_at(self.idx);
        let args = args.into_lua_args(&mut lua.0);
        let mut r = lua.0.call_value(f, &args)?;
        if r.is_empty() {
            R::from_lua_value(Value::Nil)
        } else {
            R::from_lua_value(r.remove(0))
        }
    }

    /// Call this function; return all results.
    pub fn call_multi<A>(self, lua: &mut Lua, args: A) -> Result<Vec<Value>, LuaError>
    where
        A: IntoLuaArgs,
    {
        let f = lua.0.host_root_at(self.idx);
        let args = args.into_lua_args(&mut lua.0);
        lua.0.call_value(f, &args)
    }
}

impl IntoValue for LuaFunction {
    fn into_value(self, vm: &mut Vm) -> Value {
        vm.host_root_at(self.idx)
    }
}

/// Handle to a `Value::Table` pinned in the host root pool.
#[derive(Copy, Clone, Debug)]
pub struct LuaTable {
    idx: usize,
}

impl LuaTable {
    /// Set `t[k] = v`. Both `k` and `v` may be any [`IntoValue`].
    pub fn set<K: IntoValue, V: IntoValue>(
        self,
        lua: &mut Lua,
        k: K,
        v: V,
    ) -> Result<(), LuaError> {
        let t = match lua.0.host_root_at(self.idx) {
            Value::Table(t) => t,
            _ => return Err(LuaError(Value::Nil)),
        };
        let k = k.into_value(&mut lua.0);
        let v = v.into_value(&mut lua.0);
        // SAFETY: Gc<T> is NonNull<T> over the GC heap; the heap is
        // single-threaded (see heap.rs:5-7).
        unsafe { t.as_mut() }.set(&mut lua.0.heap, k, v)?;
        lua.0
            .heap
            .barrier_back(t.as_ptr() as *mut luna_core::runtime::heap::GcHeader);
        Ok(())
    }

    /// Read `t[k]`; decode as `V`. Returns `Err` if the key is
    /// missing OR the value's type doesn't match `V`. Use
    /// `t.raw_get(k)` (returning `Value`) for runtime branching.
    pub fn get<K: IntoValue, V: FromLuaValue>(
        self,
        lua: &mut Lua,
        k: K,
    ) -> Result<V, LuaError> {
        let v = self.raw_get(lua, k)?;
        V::from_lua_value(v)
    }

    /// Read `t[k]` as a raw [`Value`] (no type coercion).
    pub fn raw_get<K: IntoValue>(self, lua: &mut Lua, k: K) -> Result<Value, LuaError> {
        let t = match lua.0.host_root_at(self.idx) {
            Value::Table(t) => t,
            _ => return Err(LuaError(Value::Nil)),
        };
        let k = k.into_value(&mut lua.0);
        // SAFETY: see set() — same single-threaded GC contract.
        Ok(unsafe { t.as_mut() }.get(k))
    }
}

impl IntoValue for LuaTable {
    fn into_value(self, vm: &mut Vm) -> Value {
        vm.host_root_at(self.idx)
    }
}

/// Generic pinned root. Use for arbitrary `Value`s the embedder
/// wants to keep alive without wrapping in `LuaFunction` / `LuaTable`.
#[derive(Copy, Clone, Debug)]
pub struct LuaRoot {
    idx: usize,
}

impl LuaRoot {
    /// Read the pinned value.
    pub fn get(self, lua: &Lua) -> Value {
        lua.0.host_root_at(self.idx)
    }
}

impl IntoValue for LuaRoot {
    fn into_value(self, vm: &mut Vm) -> Value {
        vm.host_root_at(self.idx)
    }
}

// ─────────────────────────────────────────────────────────────────────
// IntoLuaArgs — tuple to &[Value] conversion for LuaFunction::call
// ─────────────────────────────────────────────────────────────────────

/// Convert a tuple of typed values into the `&[Value]` shape
/// [`Vm::call_value`] expects. Implemented for `()` + tuples of
/// [`IntoValue`] up to arity 6.
pub trait IntoLuaArgs {
    /// Encode `self` (a tuple of [`IntoValue`] implementors) as a flat
    /// argument list ready for [`LuaFunction::call`].
    fn into_lua_args(self, vm: &mut Vm) -> Vec<Value>;
}

impl IntoLuaArgs for () {
    fn into_lua_args(self, _vm: &mut Vm) -> Vec<Value> {
        Vec::new()
    }
}

macro_rules! impl_into_lua_args_tuple {
    ( $( ($($name:ident: $idx:tt),+) ),+ $(,)? ) => {
        $(
            impl<$($name: IntoValue),+> IntoLuaArgs for ($($name,)+) {
                fn into_lua_args(self, vm: &mut Vm) -> Vec<Value> {
                    vec![ $( self.$idx.into_value(vm), )+ ]
                }
            }
        )+
    };
}
impl_into_lua_args_tuple! {
    (T0: 0),
    (T0: 0, T1: 1),
    (T0: 0, T1: 1, T2: 2),
    (T0: 0, T1: 1, T2: 2, T3: 3),
    (T0: 0, T1: 1, T2: 2, T3: 3, T4: 4),
    (T0: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5),
}