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]]
|
[[package]]
|
||||||
name = "gmod"
|
name = "gmod"
|
||||||
version = "11.1.1"
|
version = "12.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg_table 1.0.0",
|
"cfg_table 1.0.0",
|
||||||
"cstr",
|
"cstr",
|
||||||
|
@ -144,7 +144,7 @@ dependencies = [
|
||||||
"fn_abi",
|
"fn_abi",
|
||||||
"fn_has_this",
|
"fn_has_this",
|
||||||
"fn_type_alias",
|
"fn_type_alias",
|
||||||
"gmod-macros 1.0.4",
|
"gmod-macros 2.0.0",
|
||||||
"gmserverplugin",
|
"gmserverplugin",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libloading",
|
"libloading",
|
||||||
|
@ -165,7 +165,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gmod-macros"
|
name = "gmod-macros"
|
||||||
version = "1.0.4"
|
version = "2.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gmod-macros"
|
name = "gmod-macros"
|
||||||
version = "1.0.4"
|
version = "2.0.0"
|
||||||
authors = ["William Venner <william@venner.io>"]
|
authors = ["William Venner <william@venner.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -6,41 +6,60 @@ extern crate quote;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::{ReturnType, ItemFn, Attribute};
|
use syn::ItemFn;
|
||||||
|
|
||||||
fn check_lua_function(input: &mut ItemFn) {
|
fn check_lua_function(input: &mut ItemFn) {
|
||||||
assert!(input.sig.asyncness.is_none(), "Cannot be async");
|
assert!(input.sig.asyncness.is_none(), "Cannot be async");
|
||||||
assert!(input.sig.constness.is_none(), "Cannot be const");
|
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!(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");
|
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"));
|
input.sig.abi = Some(syn::parse_quote!(extern "C-unwind"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn no_mangle(item_fn: &mut ItemFn) {
|
fn genericify_return(item_fn: &mut ItemFn) {
|
||||||
item_fn.attrs.push(Attribute {
|
let stmts = std::mem::take(&mut item_fn.block.stmts);
|
||||||
path: parse_quote!(no_mangle),
|
let output = std::mem::replace(&mut item_fn.sig.output, parse_quote!(-> i32));
|
||||||
tokens: proc_macro2::TokenStream::new(),
|
item_fn.block.stmts = vec![syn::parse2(quote!({::gmod::lua::ValuesReturned::from((|| #output {#(#stmts);*})()).into()})).unwrap()];
|
||||||
style: syn::AttrStyle::Outer,
|
|
||||||
pound_token: Default::default(),
|
|
||||||
bracket_token: Default::default(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
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());
|
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()
|
input.into_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||||
|
|
||||||
|
// Make sure it's valid
|
||||||
check_lua_function(&mut input);
|
check_lua_function(&mut input);
|
||||||
|
|
||||||
|
// No mangling
|
||||||
|
input.attrs.push(parse_quote!(#[no_mangle]));
|
||||||
|
|
||||||
|
// Shutdown gmcl thread if it's running
|
||||||
#[cfg(feature = "gmcl")] {
|
#[cfg(feature = "gmcl")] {
|
||||||
let stmts = std::mem::take(&mut input.block.stmts);
|
let stmts = std::mem::take(&mut input.block.stmts);
|
||||||
input.block.stmts = vec![syn::parse2(quote!({
|
input.block.stmts = vec![syn::parse2(quote!({
|
||||||
|
@ -50,13 +69,21 @@ pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
})).unwrap()];
|
})).unwrap()];
|
||||||
}
|
}
|
||||||
|
|
||||||
no_mangle(&mut input);
|
// Make the return type nice and dynamic
|
||||||
|
genericify_return(&mut input);
|
||||||
|
|
||||||
input.into_token_stream().into()
|
input.into_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||||
|
|
||||||
|
// Make sure it's valid
|
||||||
check_lua_function(&mut input);
|
check_lua_function(&mut input);
|
||||||
|
|
||||||
|
// Make the return type nice and dynamic
|
||||||
|
genericify_return(&mut input);
|
||||||
|
|
||||||
input.into_token_stream().into()
|
input.into_token_stream().into()
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gmod"
|
name = "gmod"
|
||||||
version = "11.1.1"
|
version = "12.0.0"
|
||||||
authors = ["William Venner <william@venner.io>"]
|
authors = ["William Venner <william@venner.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -16,7 +16,7 @@ server-plugin = ["gmserverplugin"]
|
||||||
gmcl = ["gmod-macros/gmcl"]
|
gmcl = ["gmod-macros/gmcl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gmod-macros = { version = "1.0.4", path = "../gmod-macros" }
|
gmod-macros = { version = "2.0.0", path = "../gmod-macros" }
|
||||||
gmserverplugin = { version = "1", optional = true }
|
gmserverplugin = { version = "1", optional = true }
|
||||||
|
|
||||||
libloading = "0"
|
libloading = "0"
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{cell::UnsafeCell, ffi::c_void};
|
||||||
|
|
||||||
use libloading::{Library, Symbol};
|
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 LuaInt = isize;
|
||||||
pub type LuaSize = usize;
|
pub type LuaSize = usize;
|
||||||
|
@ -93,6 +93,7 @@ impl LuaSharedInterface {
|
||||||
impl std::ops::Deref for LuaSharedInterface {
|
impl std::ops::Deref for LuaSharedInterface {
|
||||||
type Target = LuaShared;
|
type Target = LuaShared;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.debug_assertions();
|
self.debug_assertions();
|
||||||
|
@ -101,6 +102,7 @@ impl std::ops::Deref for LuaSharedInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl std::ops::DerefMut for LuaSharedInterface {
|
impl std::ops::DerefMut for LuaSharedInterface {
|
||||||
|
#[inline]
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
self.debug_assertions();
|
self.debug_assertions();
|
||||||
|
|
|
@ -522,6 +522,7 @@ impl LuaState {
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
pub unsafe fn error<S: AsRef<str>>(&self, msg: S) -> ! {
|
pub unsafe fn error<S: AsRef<str>>(&self, msg: S) -> ! {
|
||||||
self.push_string(msg.as_ref());
|
self.push_string(msg.as_ref());
|
||||||
(LUA_SHARED.lua_error)(*self);
|
(LUA_SHARED.lua_error)(*self);
|
||||||
|
@ -625,6 +626,7 @@ impl LuaState {
|
||||||
impl std::ops::Deref for LuaState {
|
impl std::ops::Deref for LuaState {
|
||||||
type Target = *mut std::ffi::c_void;
|
type Target = *mut std::ffi::c_void;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
mod import;
|
mod import;
|
||||||
|
use std::cell::Cell;
|
||||||
|
|
||||||
pub use import::*;
|
pub use import::*;
|
||||||
|
|
||||||
mod lua_state;
|
mod lua_state;
|
||||||
|
@ -9,6 +11,9 @@ pub use lua_state::LuaState as State;
|
||||||
mod push;
|
mod push;
|
||||||
pub use push::*;
|
pub use push::*;
|
||||||
|
|
||||||
|
mod returns;
|
||||||
|
pub use returns::ValuesReturned;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LuaError {
|
pub enum LuaError {
|
||||||
/// Out of memory
|
/// Out of memory
|
||||||
|
@ -120,4 +125,35 @@ pub struct LuaDebug {
|
||||||
/// Loads lua_shared and imports all functions. This is already done for you if you add `#[gmod::gmod13_open]` to your `gmod13_open` function.
|
/// Loads lua_shared and imports all functions. This is already done for you if you add `#[gmod::gmod13_open]` to your `gmod13_open` function.
|
||||||
pub unsafe fn load() {
|
pub unsafe fn load() {
|
||||||
import::LUA_SHARED.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