diff --git a/Cargo.lock b/Cargo.lock index 12b5f8a9a64e..178aeb7a8235 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,6 +500,18 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "ena" version = "0.14.3" @@ -1652,6 +1664,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless", "serde", ] @@ -1678,6 +1692,7 @@ dependencies = [ "indexmap", "intern", "paths", + "postcard", "rustc-hash 2.1.1", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index ecb2686a2277..0167b6408409 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,7 @@ object = { version = "0.36.7", default-features = false, features = [ "macho", "pe", ] } +postcard = {version = "1.1.3", features = ["alloc"]} process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index dac8e0943576..b8382cd4a800 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -28,6 +28,7 @@ stdx.workspace = true span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true +postcard.workspace = true [lints] workspace = true diff --git a/crates/proc-macro-api/src/legacy_protocol.rs b/crates/proc-macro-api/src/legacy_protocol.rs index ee96b899fe57..7970ee151486 100644 --- a/crates/proc-macro-api/src/legacy_protocol.rs +++ b/crates/proc-macro-api/src/legacy_protocol.rs @@ -2,6 +2,7 @@ pub mod json; pub mod msg; +pub mod postcard_wire; use std::{ io::{BufRead, Write}, @@ -150,7 +151,11 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result, +) -> Result, ServerError> { + let bytes = postcard_wire::encode_cobs(&req) + .map_err(|_| ServerError { message: "failed to write request".into(), io: None })?; + + postcard_wire::write_postcard(&mut writer, &bytes).map_err(|err| ServerError { + message: "failed to write request".into(), + io: Some(Arc::new(err)), + })?; + + let frame = postcard_wire::read_postcard(&mut reader, buf).map_err(|err| ServerError { + message: "failed to read response".into(), + io: Some(Arc::new(err)), + })?; + + match frame { + None => Ok(None), + Some(bytes) => { + let resp: Response = postcard_wire::decode_cobs(bytes).map_err(|e| ServerError { + message: format!("failed to decode message: {e}"), + io: None, + })?; + Ok(Some(resp)) + } + } +} diff --git a/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs b/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs new file mode 100644 index 000000000000..8a1a588628d1 --- /dev/null +++ b/crates/proc-macro-api/src/legacy_protocol/postcard_wire.rs @@ -0,0 +1,25 @@ +use std::io::{self, BufRead, Write}; + +pub fn read_postcard<'a>( + input: &mut impl BufRead, + buf: &'a mut Vec, +) -> io::Result> { + buf.clear(); + let n = input.read_until(0, buf)?; + if n == 0 { + return Ok(None); + } + Ok(Some(&mut buf[..])) +} +pub fn write_postcard(out: &mut impl Write, msg: &[u8]) -> io::Result<()> { + out.write_all(msg)?; + out.flush() +} + +pub fn encode_cobs(value: &T) -> Result, postcard::Error> { + postcard::to_allocvec_cobs(value) +} + +pub fn decode_cobs(bytes: &mut [u8]) -> Result { + postcard::from_bytes_cobs(bytes) +} diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index 97919b85b513..354cb3dff46a 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -24,9 +24,10 @@ pub mod version { /// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field. pub const EXTENDED_LEAF_DATA: u32 = 5; pub const HASHED_AST_ID: u32 = 6; + pub const POSTCARD_WIRE: u32 = 7; /// Current API version of the proc-macro protocol. - pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; + pub const CURRENT_API_VERSION: u32 = POSTCARD_WIRE; } /// Represents different kinds of procedural macros that can be expanded by the external server. diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index fe274a027a80..9e86977d766c 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -31,6 +31,7 @@ pub(crate) struct ProcMacroServerProcess { #[derive(Debug)] enum Protocol { LegacyJson { mode: SpanMode }, + Postcard { mode: SpanMode }, } /// Maintains the state of the proc-macro server process. @@ -82,7 +83,11 @@ impl ProcMacroServerProcess { if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT && let Ok(mode) = srv.enable_rust_analyzer_spans() { - srv.protocol = Protocol::LegacyJson { mode }; + if srv.version >= version::POSTCARD_WIRE { + srv.protocol = Protocol::Postcard { mode }; + } else { + srv.protocol = Protocol::LegacyJson { mode }; + } } tracing::info!("Proc-macro server protocol: {:?}", srv.protocol); Ok(srv) @@ -99,6 +104,10 @@ impl ProcMacroServerProcess { self.exited.get().map(|it| &it.0) } + pub(crate) fn use_postcard(&self) -> bool { + matches!(self.protocol, Protocol::Postcard { .. }) + } + /// Retrieves the API version of the proc-macro server. pub(crate) fn version(&self) -> u32 { self.version @@ -108,6 +117,7 @@ impl ProcMacroServerProcess { pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, + Protocol::Postcard { mode } => mode == SpanMode::RustAnalyzer, } } @@ -115,6 +125,7 @@ impl ProcMacroServerProcess { fn version_check(&self) -> Result { match self.protocol { Protocol::LegacyJson { .. } => legacy_protocol::version_check(self), + Protocol::Postcard { .. } => legacy_protocol::version_check(self), } } @@ -122,6 +133,7 @@ impl ProcMacroServerProcess { fn enable_rust_analyzer_spans(&self) -> Result { match self.protocol { Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self), + Protocol::Postcard { .. } => legacy_protocol::enable_rust_analyzer_spans(self), } } @@ -132,6 +144,7 @@ impl ProcMacroServerProcess { ) -> Result, String>, ServerError> { match self.protocol { Protocol::LegacyJson { .. } => legacy_protocol::find_proc_macros(self, dylib_path), + Protocol::Postcard { .. } => legacy_protocol::find_proc_macros(self, dylib_path), } } @@ -188,6 +201,56 @@ impl ProcMacroServerProcess { } }) } + + pub(crate) fn send_task_bin( + &self, + serialize_req: impl FnOnce( + &mut dyn Write, + &mut dyn BufRead, + Request, + &mut Vec, + ) -> Result, ServerError>, + req: Request, + ) -> Result { + let state = &mut *self.state.lock().unwrap(); + let mut buf = Vec::::new(); + serialize_req(&mut state.stdin, &mut state.stdout, req, &mut buf) + .and_then(|res| { + res.ok_or_else(|| ServerError { + message: "proc-macro server did not respond with data".to_owned(), + io: Some(Arc::new(io::Error::new( + io::ErrorKind::BrokenPipe, + "proc-macro server did not respond with data", + ))), + }) + }) + .map_err(|e| { + // keep the exact BrokenPipe/exit handling from `send_task` + if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { + match state.process.child.try_wait() { + Ok(None) | Err(_) => e, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() + && let Some(stderr) = state.process.child.stderr.as_mut() + { + _ = stderr.read_to_string(&mut msg); + } + let server_error = ServerError { + message: format!( + "proc-macro server exited with {status}{}{msg}", + if msg.is_empty() { "" } else { ": " } + ), + io: None, + }; + self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone() + } + } + } else { + e + } + }) + } } /// Manages the execution of the proc-macro server process. diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index 91e9e62b084b..d5816b1bb4d1 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -14,14 +14,12 @@ publish = false proc-macro-srv.workspace = true proc-macro-api.workspace = true tt.workspace = true +postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} -postcard = { version = "1.1.3", optional = true } [features] -default = ["postcard"] sysroot-abi = ["proc-macro-srv/sysroot-abi"] in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] -postcard = ["dep:postcard"] [[bin]] diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index 662d34865eff..91d5deb7a97e 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -31,7 +31,7 @@ fn main() -> std::io::Result<()> { clap::Arg::new("format") .long("format") .action(clap::ArgAction::Set) - .default_value("json") + .default_value("postcard") .value_parser(clap::builder::EnumValueParser::::new()), clap::Arg::new("version") .long("version") @@ -51,26 +51,23 @@ fn main() -> std::io::Result<()> { #[derive(Copy, Clone)] enum ProtocolFormat { Json, - #[cfg(feature = "postcard")] Postcard, } impl ValueEnum for ProtocolFormat { fn value_variants<'a>() -> &'a [Self] { - &[ProtocolFormat::Json] + &[ProtocolFormat::Json, ProtocolFormat::Postcard] } fn to_possible_value(&self) -> Option { match self { ProtocolFormat::Json => Some(clap::builder::PossibleValue::new("json")), - #[cfg(feature = "postcard")] ProtocolFormat::Postcard => Some(clap::builder::PossibleValue::new("postcard")), } } fn from_str(input: &str, _ignore_case: bool) -> Result { match input { "json" => Ok(ProtocolFormat::Json), - #[cfg(feature = "postcard")] "postcard" => Ok(ProtocolFormat::Postcard), _ => Err(format!("unknown protocol format: {input}")), } diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs index 703bc965db25..0dd01d9ba9ef 100644 --- a/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,5 +1,5 @@ //! The main loop of the proc-macro server. -use std::io; +use std::{io, thread}; use proc_macro_api::{ legacy_protocol::{ @@ -14,6 +14,7 @@ use proc_macro_api::{ use proc_macro_srv::{EnvSnapshot, SpanId}; use crate::ProtocolFormat; +use std::io::BufReader; struct SpanTrans; @@ -37,8 +38,7 @@ impl SpanTransformer for SpanTrans { pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { match format { ProtocolFormat::Json => run_json(), - #[cfg(feature = "postcard")] - ProtocolFormat::Postcard => unimplemented!(), + ProtocolFormat::Postcard => run_postcard(), } } @@ -168,3 +168,143 @@ fn run_json() -> io::Result<()> { Ok(()) } + +fn run_postcard() -> io::Result<()> { + fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { + match kind { + proc_macro_srv::ProcMacroKind::CustomDerive => { + proc_macro_api::ProcMacroKind::CustomDerive + } + proc_macro_srv::ProcMacroKind::Bang => proc_macro_api::ProcMacroKind::Bang, + proc_macro_srv::ProcMacroKind::Attr => proc_macro_api::ProcMacroKind::Attr, + } + } + + let stdin = io::stdin(); + let stdout = io::stdout(); + let mut reader = BufReader::new(stdin.lock()); + let mut writer = stdout.lock(); + let mut buf = vec![0; 1024]; + + let env = proc_macro_srv::EnvSnapshot::default(); + let srv = proc_macro_srv::ProcMacroSrv::new(&env); + + let mut span_mode = msg::SpanMode::Id; + use proc_macro_api::legacy_protocol::postcard_wire; + + while let Some(req) = postcard_wire::read_postcard(&mut reader, &mut buf)? { + let Ok(req) = postcard_wire::decode_cobs(req) else { + thread::sleep(std::time::Duration::from_secs(1)); + continue; + }; + dbg!(&req); + + let res = match req { + msg::Request::ListMacros { dylib_path } => { + msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { + macros.into_iter().map(|(name, kind)| (name, macro_kind_to_api(kind))).collect() + })) + } + msg::Request::ExpandMacro(task) => { + let msg::ExpandMacro { + lib, + env, + current_dir, + data: + msg::ExpandMacroData { + macro_body, + macro_name, + attributes, + has_global_spans: + msg::ExpnGlobals { serialize: _, def_site, call_site, mixed_site }, + span_data_table, + }, + } = *task; + match span_mode { + msg::SpanMode::Id => msg::Response::ExpandMacro({ + let def_site = proc_macro_srv::SpanId(def_site as u32); + let call_site = proc_macro_srv::SpanId(call_site as u32); + let mixed_site = proc_macro_srv::SpanId(mixed_site as u32); + + let macro_body = + macro_body.to_subtree_unresolved::(CURRENT_API_VERSION); + let attributes = attributes + .map(|it| it.to_subtree_unresolved::(CURRENT_API_VERSION)); + + srv.expand( + lib, + &env, + current_dir, + ¯o_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + msg::FlatTree::new_raw::( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + ) + }) + .map_err(|e| e.into_string().unwrap_or_default()) + .map_err(msg::PanicMessage) + }), + msg::SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({ + let mut span_data_table = + msg::deserialize_span_data_index_map(&span_data_table); + + let def_site = span_data_table[def_site]; + let call_site = span_data_table[call_site]; + let mixed_site = span_data_table[mixed_site]; + + let macro_body = + macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table); + let attributes = attributes.map(|it| { + it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table) + }); + srv.expand( + lib, + &env, + current_dir, + ¯o_name, + macro_body, + attributes, + def_site, + call_site, + mixed_site, + ) + .map(|it| { + ( + msg::FlatTree::new( + tt::SubtreeView::new(&it), + CURRENT_API_VERSION, + &mut span_data_table, + ), + msg::serialize_span_data_index_map(&span_data_table), + ) + }) + .map(|(tree, span_data_table)| msg::ExpandMacroExtended { + tree, + span_data_table, + }) + .map_err(|e| e.into_string().unwrap_or_default()) + .map_err(msg::PanicMessage) + }), + } + } + msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION), + msg::Request::SetConfig(config) => { + span_mode = config.span_mode; + msg::Response::SetConfig(config) + } + }; + + dbg!(&res); + let res = postcard_wire::encode_cobs(&res).unwrap(); + postcard_wire::write_postcard(&mut writer, &res)?; + } + + Ok(()) +}