Compare commits
No commits in common. "94e1cf2eb0debbe1c46d92462edef29882ca7c45" and "8f6fc64f7aa643f7d65481d2c30cc8ca2c1e4bfd" have entirely different histories.
94e1cf2eb0
...
8f6fc64f7a
1307
Cargo.lock
generated
1307
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -1,6 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/lb_core",
|
|
||||||
"crates/luaffi",
|
"crates/luaffi",
|
||||||
"crates/luaffi_impl",
|
"crates/luaffi_impl",
|
||||||
"crates/luaify",
|
"crates/luaify",
|
||||||
@ -13,13 +12,4 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.40", features = ["derive"] }
|
luajit = { version = "0.1.0", path = "crates/luajit" }
|
||||||
console-subscriber = "0.4.1"
|
|
||||||
lb_core = { version = "0.1.0", path = "crates/lb_core" }
|
|
||||||
luaffi = { version = "0.1.0", path = "crates/luaffi" }
|
|
||||||
luajit = { version = "0.1.0", path = "crates/luajit", features = ["runtime"] }
|
|
||||||
mimalloc = "0.1.47"
|
|
||||||
owo-colors = "4.2.1"
|
|
||||||
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
|
||||||
tracing = "0.1.41"
|
|
||||||
tracing-subscriber = "0.3.19"
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "lb_core"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
|
||||||
luajit = { version = "0.1.0", path = "../luajit" }
|
|
||||||
owo-colors = "4.2.1"
|
|
||||||
tokio = { version = "1.45.1", features = ["full"] }
|
|
@ -1,92 +0,0 @@
|
|||||||
use luaffi::{cdef, metatype};
|
|
||||||
use luajit::State;
|
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use std::{cell::RefCell, fmt, process};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GlobalState(State);
|
|
||||||
|
|
||||||
impl GlobalState {
|
|
||||||
thread_local! {
|
|
||||||
static STATE: RefCell<Option<GlobalState>> = RefCell::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(state: State) -> Option<State> {
|
|
||||||
Self::STATE.with_borrow_mut(|s| s.replace(Self(state)).map(|s| s.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current<T>(f: impl FnOnce(&State) -> T) -> T {
|
|
||||||
Self::STATE.with_borrow(|s| f(&s.as_ref().expect("lua state not initialised").0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current_mut<T>(f: impl FnOnce(&mut State) -> T) -> T {
|
|
||||||
Self::STATE.with_borrow_mut(|s| f(&mut s.as_mut().expect("lua state not initialised").0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_thread() -> State {
|
|
||||||
Self::with_current(|s| s.new_thread())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uncaught_error(err: luajit::Error) {
|
|
||||||
let mut err = PrettyError::from(err);
|
|
||||||
if let Some(task) = tokio::task::try_id() {
|
|
||||||
err.prepend(format_args!("uncaught error in task {task}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
eprintln!("{err}");
|
|
||||||
process::abort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PrettyError {
|
|
||||||
msg: String,
|
|
||||||
trace: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrettyError {
|
|
||||||
pub fn new(msg: impl fmt::Display) -> Self {
|
|
||||||
Self {
|
|
||||||
msg: format!("{msg}"),
|
|
||||||
trace: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_trace(mut self, trace: impl fmt::Display) -> Self {
|
|
||||||
self.trace = Some(format!("{trace}"));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepend(&mut self, msg: impl fmt::Display) -> &mut Self {
|
|
||||||
if self.msg.is_empty() {
|
|
||||||
self.msg = format!("{msg}");
|
|
||||||
} else {
|
|
||||||
self.msg = format!("{msg}:\n{}", self.msg);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<luajit::Error> for PrettyError {
|
|
||||||
fn from(value: luajit::Error) -> Self {
|
|
||||||
match value {
|
|
||||||
luajit::Error::Resume { msg, trace } => Self::new(msg).with_trace(trace),
|
|
||||||
err => Self::new(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for PrettyError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self.trace {
|
|
||||||
Some(ref trace) => write!(f, "{}\n{trace}", self.msg.red()),
|
|
||||||
None => write!(f, "{}", self.msg.red()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_core;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_core {}
|
|
@ -32,7 +32,7 @@ unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CACHE_LIBS: &[(&str, &str)] = &[
|
const CACHE_LIBS: &[(&str, &str)] = &[
|
||||||
// libs in global
|
// preloaded
|
||||||
("table", "table"),
|
("table", "table"),
|
||||||
("string", "string"),
|
("string", "string"),
|
||||||
("math", "math"),
|
("math", "math"),
|
||||||
@ -143,18 +143,13 @@ impl Registry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
pub fn declare<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||||
self.include::<T>()
|
self.include::<T>();
|
||||||
.funcs
|
self.funcs
|
||||||
.insert(name.to_string())
|
.insert(name.to_string())
|
||||||
.then(|| writeln!(self.cdef, "{};", T::cdecl(name)).unwrap());
|
.then(|| writeln!(self.cdef, "{};", T::cdecl(name)).unwrap());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
|
||||||
self.include::<T>();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn done(&self) -> String {
|
pub fn done(&self) -> String {
|
||||||
self.to_string()
|
self.to_string()
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ edition = "2024"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
darling = "0.20.11"
|
|
||||||
proc-macro2 = "1.0.95"
|
proc-macro2 = "1.0.95"
|
||||||
quote = "1.0.40"
|
quote = "1.0.40"
|
||||||
syn = { version = "2.0.103", features = ["full", "visit-mut"] }
|
syn = { version = "2.0.103", features = ["full", "visit-mut"] }
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
use crate::utils::{ffi_crate, syn_assert, syn_error};
|
use crate::utils::{ffi_crate, syn_assert, syn_error};
|
||||||
use darling::FromMeta;
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{spanned::*, *};
|
use syn::{spanned::*, *};
|
||||||
|
|
||||||
#[derive(Debug, FromMeta)]
|
pub fn transform(mut item: Item) -> Result<TokenStream> {
|
||||||
pub struct Args {}
|
|
||||||
|
|
||||||
pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
|
||||||
let (name, impl_type, impl_cdef) = match item {
|
let (name, impl_type, impl_cdef) = match item {
|
||||||
Item::Struct(ref mut str) => (
|
Item::Struct(ref mut str) => (
|
||||||
str.ident.clone(),
|
str.ident.clone(),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use darling::{FromMeta, ast::NestedMeta};
|
|
||||||
use proc_macro::TokenStream as TokenStream1;
|
use proc_macro::TokenStream as TokenStream1;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::parse_macro_input;
|
use syn::parse_macro_input;
|
||||||
@ -9,9 +8,7 @@ mod utils;
|
|||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
||||||
NestedMeta::parse_meta_list(args.into())
|
cdef::transform(parse_macro_input!(input))
|
||||||
.and_then(|meta| cdef::Args::from_list(&meta).map_err(Into::into))
|
|
||||||
.and_then(|args| cdef::transform(args, syn::parse(input)?))
|
|
||||||
.unwrap_or_else(|err| err.into_compile_error().into_token_stream())
|
.unwrap_or_else(|err| err.into_compile_error().into_token_stream())
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -26,36 +26,25 @@ pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||||
let ty = imp.self_ty.clone();
|
|
||||||
let ty_name = ty_name(&ty)?;
|
|
||||||
|
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let ffi_funcs = get_ffi_functions(imp)?;
|
let ffi_funcs = get_ffi_functions(imp)?;
|
||||||
let ffi_wrappers: Vec<_> = ffi_funcs
|
let ffi_wrappers: Vec<_> = ffi_funcs
|
||||||
.iter()
|
.iter()
|
||||||
.map(generate_ffi_wrapper)
|
.map(generate_ffi_wrapper)
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
let ffi_register: Vec<_> = ffi_funcs
|
let ffi_register: Vec<_> = ffi_funcs
|
||||||
.iter()
|
.iter()
|
||||||
.map(generate_ffi_register)
|
.map(generate_ffi_register)
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
let ffi_drop_fn = format_ident!("__ffi_drop");
|
|
||||||
let ffi_drop_name = format!("{ty_name}_drop");
|
|
||||||
|
|
||||||
let ffi_exports = {
|
|
||||||
let mut names = vec![&ffi_drop_fn];
|
|
||||||
names.extend(ffi_funcs.iter().map(|f| &f.rust_name));
|
|
||||||
generate_ffi_exports(&ty, names.into_iter())?
|
|
||||||
};
|
|
||||||
|
|
||||||
let lua_funcs = get_lua_functions(imp)?;
|
let lua_funcs = get_lua_functions(imp)?;
|
||||||
let lua_register: Vec<_> = lua_funcs
|
let lua_register: Vec<_> = lua_funcs
|
||||||
.iter()
|
.iter()
|
||||||
.map(generate_lua_register)
|
.map(generate_lua_register)
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
let ty = &*imp.self_ty;
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
unsafe impl #ffi::Metatype for #ty {
|
unsafe impl #ffi::Metatype for #ty {
|
||||||
type Target = Self;
|
type Target = Self;
|
||||||
@ -63,22 +52,10 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
|||||||
fn build(b: &mut #ffi::MetatypeBuilder) {
|
fn build(b: &mut #ffi::MetatypeBuilder) {
|
||||||
#(#ffi_register)*
|
#(#ffi_register)*
|
||||||
#(#lua_register)*
|
#(#lua_register)*
|
||||||
|
|
||||||
b.declare::<unsafe extern "C" fn(*mut Self)>(#ffi_drop_name);
|
|
||||||
b.metatable_raw("gc", ::std::format_args!("C.{}", #ffi_drop_name));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl #ty {
|
impl #ty { #(#ffi_wrappers)* }
|
||||||
#(#ffi_wrappers)*
|
|
||||||
|
|
||||||
#[unsafe(export_name = #ffi_drop_name)]
|
|
||||||
unsafe extern "C" fn #ffi_drop_fn(&mut self) {
|
|
||||||
unsafe { ::std::ptr::drop_in_place(self) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ffi_exports
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,19 +213,6 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_ffi_exports<'a>(
|
|
||||||
ty: &Type,
|
|
||||||
names: impl Iterator<Item = &'a Ident>,
|
|
||||||
) -> Result<TokenStream> {
|
|
||||||
Ok(quote! {
|
|
||||||
// hack to prevent ffi functions from being dead code-eliminated
|
|
||||||
#[used]
|
|
||||||
static __FFI_EXPORTS: &[fn()] = unsafe {
|
|
||||||
&[#(::std::mem::transmute(#ty::#names as *const ())),*]
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LuaFunction {
|
struct LuaFunction {
|
||||||
name: String,
|
name: String,
|
||||||
params: Vec<PatType>,
|
params: Vec<PatType>,
|
||||||
|
167
src/main.rs
167
src/main.rs
@ -1,168 +1,3 @@
|
|||||||
use clap::Parser;
|
|
||||||
use lb_core::{GlobalState, PrettyError};
|
|
||||||
use mimalloc::MiMalloc;
|
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use std::{backtrace::Backtrace, net::SocketAddr, num::NonZero, panic, thread};
|
|
||||||
use tokio::{runtime, task::LocalSet};
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
|
||||||
|
|
||||||
fn panic_cb(panic: &panic::PanicHookInfo) {
|
|
||||||
let trace = Backtrace::force_capture();
|
|
||||||
let location = panic.location().unwrap();
|
|
||||||
let payload = panic.payload();
|
|
||||||
let msg = if let Some(s) = payload.downcast_ref::<&'static str>() {
|
|
||||||
s
|
|
||||||
} else if let Some(s) = payload.downcast_ref::<String>() {
|
|
||||||
s.as_str()
|
|
||||||
} else {
|
|
||||||
"unknown error"
|
|
||||||
};
|
|
||||||
|
|
||||||
eprint!(
|
|
||||||
"{}",
|
|
||||||
PrettyError::new(msg)
|
|
||||||
.with_trace(trace)
|
|
||||||
.prepend(format_args!(
|
|
||||||
"thread '{}' panicked at {location}",
|
|
||||||
thread::current().name().unwrap_or("<unnamed>")
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
struct Args {
|
|
||||||
/// Paths to scripts to execute.
|
|
||||||
#[clap(value_name = "SCRIPTS")]
|
|
||||||
paths: Vec<String>,
|
|
||||||
|
|
||||||
/// Strings to execute.
|
|
||||||
#[clap(long, short = 'e', value_name = "CHUNK")]
|
|
||||||
evals: Vec<String>,
|
|
||||||
|
|
||||||
/// Libraries to require on startup.
|
|
||||||
#[clap(long, short = 'l', value_name = "NAME")]
|
|
||||||
libs: Vec<String>,
|
|
||||||
|
|
||||||
/// Console log level.
|
|
||||||
#[clap(long, value_name = "LEVEL", default_value = "debug")]
|
|
||||||
log_level: tracing::Level,
|
|
||||||
|
|
||||||
/// Number of runtime worker threads.
|
|
||||||
#[clap(long, value_name = "THREADS", default_value_t = Self::threads())]
|
|
||||||
threads: NonZero<usize>,
|
|
||||||
|
|
||||||
/// Number of runtime blocking threads.
|
|
||||||
#[clap(long, value_name = "THREADS", default_value_t = Self::blocking_threads())]
|
|
||||||
blocking_threads: NonZero<usize>,
|
|
||||||
|
|
||||||
/// Enable tokio-console integration.
|
|
||||||
#[clap(long)]
|
|
||||||
enable_console: bool,
|
|
||||||
|
|
||||||
/// tokio-console publish address.
|
|
||||||
#[clap(long, value_name = "ADDRESS", default_value = "127.0.0.1:6669")]
|
|
||||||
console_addr: SocketAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Args {
|
|
||||||
fn threads() -> NonZero<usize> {
|
|
||||||
thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blocking_threads() -> NonZero<usize> {
|
|
||||||
NonZero::new(1024).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
panic::set_hook(Box::new(panic_cb));
|
println!("Hello, world!");
|
||||||
|
|
||||||
let args = Args::parse();
|
|
||||||
init_logger(&args);
|
|
||||||
let runtime = init_runtime(&args);
|
|
||||||
GlobalState::set(init_vm(&args));
|
|
||||||
|
|
||||||
let main = LocalSet::new();
|
|
||||||
main.spawn_local(run(args));
|
|
||||||
runtime.block_on(main);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_logger(args: &Args) {
|
|
||||||
use tracing::level_filters::LevelFilter;
|
|
||||||
use tracing_subscriber::{Layer, util::*};
|
|
||||||
|
|
||||||
let console = tracing_subscriber::fmt()
|
|
||||||
.compact()
|
|
||||||
.with_env_filter(
|
|
||||||
tracing_subscriber::EnvFilter::builder()
|
|
||||||
.with_default_directive(LevelFilter::from(args.log_level).into())
|
|
||||||
.from_env_lossy(),
|
|
||||||
)
|
|
||||||
.with_file(false)
|
|
||||||
.with_line_number(false)
|
|
||||||
.with_target(false)
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
if args.enable_console {
|
|
||||||
console_subscriber::ConsoleLayer::builder()
|
|
||||||
.with_default_env()
|
|
||||||
.server_addr(args.console_addr)
|
|
||||||
.spawn()
|
|
||||||
.with_subscriber(console)
|
|
||||||
.init()
|
|
||||||
} else {
|
|
||||||
console.init()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_runtime(args: &Args) -> runtime::Runtime {
|
|
||||||
if args.threads.get() == 1 {
|
|
||||||
runtime::Builder::new_current_thread()
|
|
||||||
} else {
|
|
||||||
runtime::Builder::new_multi_thread()
|
|
||||||
}
|
|
||||||
.enable_all()
|
|
||||||
.thread_name("lb")
|
|
||||||
.worker_threads(args.threads.get() - 1)
|
|
||||||
.max_blocking_threads(args.blocking_threads.get())
|
|
||||||
.build()
|
|
||||||
.unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_vm(_args: &Args) -> luajit::State {
|
|
||||||
let mut state =
|
|
||||||
luajit::State::new().unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"));
|
|
||||||
|
|
||||||
let mut registry = luaffi::Registry::new();
|
|
||||||
registry.include::<lb_core::lb_core>();
|
|
||||||
|
|
||||||
println!("{registry}");
|
|
||||||
|
|
||||||
state
|
|
||||||
.load(Some("@[luby]"), registry.done(), luajit::LoadMode::TEXT)
|
|
||||||
.and_then(|()| state.call(0, 0))
|
|
||||||
.unwrap_or_else(|err| panic!("failed to load modules: {err}"));
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(args: Args) {
|
|
||||||
let mut state = GlobalState::new_thread();
|
|
||||||
for ref path in args.paths {
|
|
||||||
let chunk = match std::fs::read(path) {
|
|
||||||
Ok(chunk) => chunk,
|
|
||||||
Err(err) => return eprintln!("{}", format!("{path}: {err}").red()),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Err(err) = state.load(Some(format!("@{path}")), chunk, Default::default()) {
|
|
||||||
return eprintln!("{}", err.red());
|
|
||||||
}
|
|
||||||
|
|
||||||
state
|
|
||||||
.call_async(0)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(GlobalState::uncaught_error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user