use luna_core::runtime::Value;
use luna_core::version::LuaVersion;
use luna_core::vm::{
FromLuaValue, HostRootStale, HostRootTicket, IntoValue, LuaError, NativeTypedSig,
SandboxBuilder, Vm,
};
pub struct Lua(Vm);
impl Lua {
pub fn new() -> Lua {
Lua(crate::new_minimal_with_jit(LuaVersion::Lua55))
}
pub fn with_version(v: LuaVersion) -> Lua {
Lua(crate::new_minimal_with_jit(v))
}
pub fn sandbox(v: LuaVersion) -> LuaSandboxBuilder {
LuaSandboxBuilder {
inner: Vm::sandbox(v),
}
}
pub fn vm(&mut self) -> &mut Vm {
&mut self.0
}
pub fn open_base(&mut self) {
self.0.open_base();
}
pub fn open_math(&mut self) {
self.0.open_math();
}
pub fn open_string(&mut self) {
self.0.open_string();
}
pub fn open_table(&mut self) {
self.0.open_table();
}
pub fn open_coroutine(&mut self) {
self.0.open_coroutine();
}
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))
}
}
pub fn eval_multi(&mut self, src: &str) -> Result<Vec<Value>, LuaError> {
self.0.eval(src)
}
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))
}
}
pub async fn eval_async_multi(&mut self, src: &str) -> Result<Vec<Value>, LuaError> {
self.0.eval_async(src).await
}
pub fn set_async_native(
&mut self,
name: &str,
f: luna_core::vm::AsyncNativeFn,
) -> Result<(), LuaError> {
self.0.set_async_native(name, f)
}
pub fn set_global<V: IntoValue>(&mut self, name: &str, v: V) -> Result<(), LuaError> {
self.0.set_global(name, v)
}
pub fn globals(&mut self) -> LuaTable {
let g = self.0.globals();
let ticket = self.0.pin_host(Value::Table(g));
LuaTable { ticket }
}
pub fn create_table(&mut self) -> LuaTable {
let t = self.0.new_table().build();
let ticket = self.0.pin_host(Value::Table(t));
LuaTable { ticket }
}
pub fn create_function<F, Marker>(&mut self, f: F) -> LuaFunction
where
F: NativeTypedSig<Marker>,
{
let v = self.0.native_typed(f);
let ticket = self.0.pin_host(v);
LuaFunction { ticket }
}
pub fn pin<V: IntoValue>(&mut self, v: V) -> LuaRoot {
let v = v.into_value(&mut self.0);
let ticket = self.0.pin_host(v);
LuaRoot { ticket }
}
pub fn unpin<H: PinnedHandle>(&mut self, h: H) -> Result<(), HostRootStale> {
self.0.unpin(h.ticket())
}
pub fn unpin_all(&mut self) {
self.0.unpin_all();
}
pub fn pinned_count(&self) -> usize {
self.0.host_root_count()
}
}
pub trait PinnedHandle {
fn ticket(&self) -> HostRootTicket;
}
impl Default for Lua {
fn default() -> Self {
Lua::new()
}
}
pub struct LuaSandboxBuilder {
inner: SandboxBuilder,
}
impl LuaSandboxBuilder {
pub fn open_base(mut self) -> Self {
self.inner = self.inner.open_base();
self
}
pub fn open_math(mut self) -> Self {
self.inner = self.inner.open_math();
self
}
pub fn open_string(mut self) -> Self {
self.inner = self.inner.open_string();
self
}
pub fn open_table(mut self) -> Self {
self.inner = self.inner.open_table();
self
}
pub fn open_coroutine(mut self) -> Self {
self.inner = self.inner.open_coroutine();
self
}
pub fn with_instr_budget(mut self, n: i64) -> Self {
self.inner = self.inner.with_instr_budget(n);
self
}
pub fn with_memory_cap(mut self, n: usize) -> Self {
self.inner = self.inner.with_memory_cap(n);
self
}
pub fn allow_bytecode_loading(mut self) -> Self {
self.inner = self.inner.allow_bytecode_loading();
self
}
pub fn build(self) -> Lua {
Lua(self.inner.build())
}
}
#[derive(Copy, Clone, Debug)]
pub struct LuaFunction {
ticket: HostRootTicket,
}
impl LuaFunction {
pub fn ticket(self) -> HostRootTicket {
self.ticket
}
pub fn call<A, R>(self, lua: &mut Lua, args: A) -> Result<R, LuaError>
where
A: IntoLuaArgs,
R: FromLuaValue,
{
let f = lua
.0
.read_host(self.ticket)
.expect("LuaFunction used after unpin / unpin_all");
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))
}
}
pub fn call_multi<A>(self, lua: &mut Lua, args: A) -> Result<Vec<Value>, LuaError>
where
A: IntoLuaArgs,
{
let f = lua
.0
.read_host(self.ticket)
.expect("LuaFunction used after unpin / unpin_all");
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.read_host(self.ticket)
.expect("LuaFunction used after unpin / unpin_all")
}
}
impl PinnedHandle for LuaFunction {
fn ticket(&self) -> HostRootTicket {
self.ticket
}
}
#[derive(Copy, Clone, Debug)]
pub struct LuaTable {
ticket: HostRootTicket,
}
impl LuaTable {
pub fn ticket(self) -> HostRootTicket {
self.ticket
}
pub fn set<K: IntoValue, V: IntoValue>(
self,
lua: &mut Lua,
k: K,
v: V,
) -> Result<(), LuaError> {
let t = match lua
.0
.read_host(self.ticket)
.expect("LuaTable used after unpin / unpin_all")
{
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);
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(())
}
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)
}
pub fn raw_get<K: IntoValue>(self, lua: &mut Lua, k: K) -> Result<Value, LuaError> {
let t = match lua
.0
.read_host(self.ticket)
.expect("LuaTable used after unpin / unpin_all")
{
Value::Table(t) => t,
_ => return Err(LuaError(Value::Nil)),
};
let k = k.into_value(&mut lua.0);
Ok(unsafe { t.as_mut() }.get(k))
}
}
impl IntoValue for LuaTable {
fn into_value(self, vm: &mut Vm) -> Value {
vm.read_host(self.ticket)
.expect("LuaTable used after unpin / unpin_all")
}
}
impl PinnedHandle for LuaTable {
fn ticket(&self) -> HostRootTicket {
self.ticket
}
}
#[derive(Copy, Clone, Debug)]
pub struct LuaRoot {
ticket: HostRootTicket,
}
impl LuaRoot {
pub fn ticket(self) -> HostRootTicket {
self.ticket
}
pub fn get(self, lua: &Lua) -> Value {
lua.0
.read_host(self.ticket)
.expect("LuaRoot used after unpin / unpin_all")
}
}
impl IntoValue for LuaRoot {
fn into_value(self, vm: &mut Vm) -> Value {
vm.read_host(self.ticket)
.expect("LuaRoot used after unpin / unpin_all")
}
}
impl PinnedHandle for LuaRoot {
fn ticket(&self) -> HostRootTicket {
self.ticket
}
}
pub trait IntoLuaArgs {
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),
}