Working commit
This commit is contained in:
parent
f9676a1436
commit
94e1cf2eb0
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,5 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"crates/lb_core",
|
||||||
"crates/luaffi",
|
"crates/luaffi",
|
||||||
"crates/luaffi_impl",
|
"crates/luaffi_impl",
|
||||||
"crates/luaify",
|
"crates/luaify",
|
||||||
@ -12,4 +13,13 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
luajit = { version = "0.1.0", path = "crates/luajit" }
|
clap = { version = "4.5.40", features = ["derive"] }
|
||||||
|
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"
|
||||||
|
10
crates/lb_core/Cargo.toml
Normal file
10
crates/lb_core/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[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"] }
|
92
crates/lb_core/src/lib.rs
Normal file
92
crates/lb_core/src/lib.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
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)] = &[
|
||||||
// preloaded
|
// libs in global
|
||||||
("table", "table"),
|
("table", "table"),
|
||||||
("string", "string"),
|
("string", "string"),
|
||||||
("math", "math"),
|
("math", "math"),
|
||||||
@ -143,13 +143,18 @@ 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>()
|
||||||
self.funcs
|
.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()
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,9 @@ use quote::{format_ident, quote};
|
|||||||
use syn::{spanned::*, *};
|
use syn::{spanned::*, *};
|
||||||
|
|
||||||
#[derive(Debug, FromMeta)]
|
#[derive(Debug, FromMeta)]
|
||||||
pub struct Args {
|
pub struct Args {}
|
||||||
module: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transform(args: Args, mut item: Item) -> Result<TokenStream> {
|
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(),
|
||||||
|
169
src/main.rs
169
src/main.rs
@ -1,3 +1,168 @@
|
|||||||
fn main() {
|
use clap::Parser;
|
||||||
println!("Hello, world!");
|
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() {
|
||||||
|
panic::set_hook(Box::new(panic_cb));
|
||||||
|
|
||||||
|
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