Lua functions can now return Result, Option<NonZeroI32>, () or i32
This commit is contained in:
parent
47492748f2
commit
9b79d402e9
|
@ -135,7 +135,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gmod"
|
||||
version = "11.1.1"
|
||||
version = "12.0.0"
|
||||
dependencies = [
|
||||
"cfg_table 1.0.0",
|
||||
"cstr",
|
||||
|
@ -144,7 +144,7 @@ dependencies = [
|
|||
"fn_abi",
|
||||
"fn_has_this",
|
||||
"fn_type_alias",
|
||||
"gmod-macros 1.0.4",
|
||||
"gmod-macros 2.0.0",
|
||||
"gmserverplugin",
|
||||
"lazy_static",
|
||||
"libloading",
|
||||
|
@ -165,7 +165,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gmod-macros"
|
||||
version = "1.0.4"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gmod-macros"
|
||||
version = "1.0.4"
|
||||
version = "2.0.0"
|
||||
authors = ["William Venner <william@venner.io>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
|
|
@ -6,41 +6,60 @@ extern crate quote;
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{ReturnType, ItemFn, Attribute};
|
||||
use syn::ItemFn;
|
||||
|
||||
fn check_lua_function(input: &mut ItemFn) {
|
||||
assert!(input.sig.asyncness.is_none(), "Cannot be async");
|
||||
assert!(input.sig.constness.is_none(), "Cannot be const");
|
||||
assert!(input.sig.inputs.len() == 1, "There can only be one argument, and it should be a pointer to the Lua state (gmod::lua::State)");
|
||||
assert!(matches!(&input.sig.output, ReturnType::Type(_, r#type) if r#type.to_token_stream().to_string() == "i32"), "The output must be an i32, representing the number of return values of the function");
|
||||
assert!(input.sig.abi.is_none() || input.sig.abi.as_ref().and_then(|abi| abi.name.as_ref()).map(|abi| abi.value() == "C-unwind").unwrap_or(true), "Do not specify an ABI");
|
||||
input.sig.abi = Some(syn::parse_quote!(extern "C-unwind"));
|
||||
}
|
||||
|
||||
fn no_mangle(item_fn: &mut ItemFn) {
|
||||
item_fn.attrs.push(Attribute {
|
||||
path: parse_quote!(no_mangle),
|
||||
tokens: proc_macro2::TokenStream::new(),
|
||||
style: syn::AttrStyle::Outer,
|
||||
pound_token: Default::default(),
|
||||
bracket_token: Default::default(),
|
||||
});
|
||||
fn genericify_return(item_fn: &mut ItemFn) {
|
||||
let stmts = std::mem::take(&mut item_fn.block.stmts);
|
||||
let output = std::mem::replace(&mut item_fn.sig.output, parse_quote!(-> i32));
|
||||
item_fn.block.stmts = vec![syn::parse2(quote!({::gmod::lua::ValuesReturned::from((|| #output {#(#stmts);*})()).into()})).unwrap()];
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||
check_lua_function(&mut input);
|
||||
|
||||
let lua_ident = format_ident!("{}", match &input.sig.inputs[0] {
|
||||
syn::FnArg::Typed(arg) => arg.pat.to_token_stream().to_string(),
|
||||
_ => unreachable!(),
|
||||
});
|
||||
|
||||
// Capture the Lua state
|
||||
input.block.stmts.insert(0, syn::parse2(quote!(::gmod::lua::__set_state__internal(#lua_ident);)).unwrap());
|
||||
|
||||
// Load lua_shared
|
||||
input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
|
||||
no_mangle(&mut input);
|
||||
|
||||
// Make sure it's valid
|
||||
check_lua_function(&mut input);
|
||||
|
||||
// No mangling
|
||||
input.attrs.push(parse_quote!(#[no_mangle]));
|
||||
|
||||
// Make the return type nice and dynamic
|
||||
genericify_return(&mut input);
|
||||
|
||||
input.into_token_stream().into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||
|
||||
// Make sure it's valid
|
||||
check_lua_function(&mut input);
|
||||
|
||||
// No mangling
|
||||
input.attrs.push(parse_quote!(#[no_mangle]));
|
||||
|
||||
// Shutdown gmcl thread if it's running
|
||||
#[cfg(feature = "gmcl")] {
|
||||
let stmts = std::mem::take(&mut input.block.stmts);
|
||||
input.block.stmts = vec![syn::parse2(quote!({
|
||||
|
@ -50,13 +69,21 @@ pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
|||
})).unwrap()];
|
||||
}
|
||||
|
||||
no_mangle(&mut input);
|
||||
// Make the return type nice and dynamic
|
||||
genericify_return(&mut input);
|
||||
|
||||
input.into_token_stream().into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||
|
||||
// Make sure it's valid
|
||||
check_lua_function(&mut input);
|
||||
|
||||
// Make the return type nice and dynamic
|
||||
genericify_return(&mut input);
|
||||
|
||||
input.into_token_stream().into()
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gmod"
|
||||
version = "11.1.1"
|
||||
version = "12.0.0"
|
||||
authors = ["William Venner <william@venner.io>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
@ -16,7 +16,7 @@ server-plugin = ["gmserverplugin"]
|
|||
gmcl = ["gmod-macros/gmcl"]
|
||||
|
||||
[dependencies]
|
||||
gmod-macros = { version = "1.0.4", path = "../gmod-macros" }
|
||||
gmod-macros = { version = "2.0.0", path = "../gmod-macros" }
|
||||
gmserverplugin = { version = "1", optional = true }
|
||||
|
||||
libloading = "0"
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{cell::UnsafeCell, ffi::c_void};
|
|||
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
use super::{LuaError, State as LuaState, LuaDebug};
|
||||
use super::{LuaError, State as LuaState, LuaDebug, returns::ValuesReturned};
|
||||
|
||||
pub type LuaInt = isize;
|
||||
pub type LuaSize = usize;
|
||||
|
@ -93,6 +93,7 @@ impl LuaSharedInterface {
|
|||
impl std::ops::Deref for LuaSharedInterface {
|
||||
type Target = LuaShared;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
#[cfg(debug_assertions)]
|
||||
self.debug_assertions();
|
||||
|
@ -101,6 +102,7 @@ impl std::ops::Deref for LuaSharedInterface {
|
|||
}
|
||||
}
|
||||
impl std::ops::DerefMut for LuaSharedInterface {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
#[cfg(debug_assertions)]
|
||||
self.debug_assertions();
|
||||
|
|
|
@ -522,6 +522,7 @@ impl LuaState {
|
|||
ptr
|
||||
}
|
||||
|
||||
#[cold]
|
||||
pub unsafe fn error<S: AsRef<str>>(&self, msg: S) -> ! {
|
||||
self.push_string(msg.as_ref());
|
||||
(LUA_SHARED.lua_error)(*self);
|
||||
|
@ -625,6 +626,7 @@ impl LuaState {
|
|||
impl std::ops::Deref for LuaState {
|
||||
type Target = *mut std::ffi::c_void;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#![allow(unused)]
|
||||
|
||||
mod import;
|
||||
use std::cell::Cell;
|
||||
|
||||
pub use import::*;
|
||||
|
||||
mod lua_state;
|
||||
|
@ -9,6 +11,9 @@ pub use lua_state::LuaState as State;
|
|||
mod push;
|
||||
pub use push::*;
|
||||
|
||||
mod returns;
|
||||
pub use returns::ValuesReturned;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LuaError {
|
||||
/// Out of memory
|
||||
|
@ -121,3 +126,34 @@ pub struct LuaDebug {
|
|||
pub unsafe fn load() {
|
||||
import::LUA_SHARED.load()
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
#[cfg(debug_assertions)]
|
||||
static LUA: Cell<Option<State>> = Cell::new(None);
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
static LUA: Cell<State> = Cell::new(State(std::ptr::null_mut()));
|
||||
}
|
||||
/// Acquires a pointer to the Lua state for the current thread.
|
||||
///
|
||||
/// This will panic if called from anywhere but the main thread. This will panic if you are not using the `#[gmod13_open]` macro to open the Lua state.
|
||||
///
|
||||
/// This will NOT panic in release mode under these conditions and will instead cause undefined behaviour.
|
||||
pub unsafe fn state() -> State {
|
||||
LUA.with(|cell| {
|
||||
#[cfg(debug_assertions)] {
|
||||
cell.get().expect("The Lua state cannot be found in this thread. Perhaps you are calling this function from a thread other than the main thread? Perhaps you forgot to use the `#[gmod13_open]` macro?")
|
||||
}
|
||||
#[cfg(not(debug_assertions))] {
|
||||
cell.get()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(non_snake_case)]
|
||||
pub fn __set_state__internal(state: State) {
|
||||
LUA.with(|cell| {
|
||||
cell.set(Some(state));
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
use std::num::NonZeroI32;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ValuesReturned(pub i32);
|
||||
|
||||
impl Into<i32> for ValuesReturned {
|
||||
#[inline(always)]
|
||||
fn into(self) -> i32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for ValuesReturned {
|
||||
#[inline(always)]
|
||||
fn from(n: i32) -> Self {
|
||||
ValuesReturned(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NonZeroI32> for ValuesReturned {
|
||||
#[inline(always)]
|
||||
fn from(n: NonZeroI32) -> ValuesReturned {
|
||||
ValuesReturned(i32::from(n))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for ValuesReturned {
|
||||
#[inline(always)]
|
||||
fn from(_: ()) -> ValuesReturned {
|
||||
ValuesReturned(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<NonZeroI32>> for ValuesReturned {
|
||||
#[inline(always)]
|
||||
fn from(opt: Option<NonZeroI32>) -> ValuesReturned {
|
||||
ValuesReturned(match opt {
|
||||
Some(vals) => i32::from(vals),
|
||||
None => {
|
||||
unsafe { super::state().push_nil() };
|
||||
1
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: std::fmt::Debug> From<Result<i32, E>> for ValuesReturned {
|
||||
#[inline(always)]
|
||||
fn from(res: Result<i32, E>) -> ValuesReturned {
|
||||
match res {
|
||||
Ok(vals) => ValuesReturned(vals),
|
||||
Err(err) => unsafe { super::state().error(&format!("{:?}", err)) }
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue