From af9bff5b611980655b3393c7ab14881981ef0c91 Mon Sep 17 00:00:00 2001 From: maciektr Date: Mon, 19 Feb 2024 17:13:45 +0100 Subject: [PATCH] Split cairo-lang-macro crate into two (api <> abi) commit-id:54582a03 --- Cargo.lock | 11 ++ Cargo.toml | 3 +- .../cairo-lang-macro-attributes/src/lib.rs | 6 +- plugins/cairo-lang-macro-stable/Cargo.toml | 18 +++ plugins/cairo-lang-macro-stable/README.md | 3 + plugins/cairo-lang-macro-stable/src/lib.rs | 51 +++++++ plugins/cairo-lang-macro/Cargo.toml | 1 + plugins/cairo-lang-macro/src/lib.rs | 104 ++++++++++++++- plugins/cairo-lang-macro/src/stable_abi.rs | 124 ------------------ scarb/Cargo.toml | 1 + scarb/src/compiler/plugin/proc_macro/ffi.rs | 6 +- scarb/tests/build_cairo_plugin.rs | 10 +- 12 files changed, 201 insertions(+), 137 deletions(-) create mode 100644 plugins/cairo-lang-macro-stable/Cargo.toml create mode 100644 plugins/cairo-lang-macro-stable/README.md create mode 100644 plugins/cairo-lang-macro-stable/src/lib.rs delete mode 100644 plugins/cairo-lang-macro/src/stable_abi.rs diff --git a/Cargo.lock b/Cargo.lock index 06faaf0d7..be23202b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -731,6 +731,7 @@ version = "0.0.1" dependencies = [ "anyhow", "cairo-lang-macro-attributes", + "cairo-lang-macro-stable", "serde", "serde_json", ] @@ -744,6 +745,15 @@ dependencies = [ "syn 2.0.49", ] +[[package]] +name = "cairo-lang-macro-stable" +version = "0.0.1" +dependencies = [ + "anyhow", + "serde", + "serde_json", +] + [[package]] name = "cairo-lang-parser" version = "2.5.4" @@ -4446,6 +4456,7 @@ dependencies = [ "cairo-lang-filesystem", "cairo-lang-formatter", "cairo-lang-macro", + "cairo-lang-macro-stable", "cairo-lang-semantic", "cairo-lang-sierra", "cairo-lang-sierra-to-casm", diff --git a/Cargo.toml b/Cargo.toml index 6223a9f80..74bc1cae3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,9 @@ members = [ "extensions/scarb-cairo-run", "extensions/scarb-cairo-test", "extensions/scarb-snforge-test-collector", - "plugins/cairo-lang-macro-attributes", "plugins/cairo-lang-macro", + "plugins/cairo-lang-macro-attributes", + "plugins/cairo-lang-macro-stable", "utils/create-output-dir", "utils/scarb-build-metadata", "utils/scarb-test-support", diff --git a/plugins/cairo-lang-macro-attributes/src/lib.rs b/plugins/cairo-lang-macro-attributes/src/lib.rs index 5a4d6ca29..81856948f 100644 --- a/plugins/cairo-lang-macro-attributes/src/lib.rs +++ b/plugins/cairo-lang-macro-attributes/src/lib.rs @@ -16,10 +16,10 @@ pub fn attribute_macro(_args: TokenStream, input: TokenStream) -> TokenStream { #item #[no_mangle] - pub unsafe extern "C" fn expand(token_stream: cairo_lang_macro::stable_abi::StableTokenStream) -> cairo_lang_macro::stable_abi::StableProcMacroResult { - let token_stream = token_stream.into_token_stream(); + pub unsafe extern "C" fn expand(token_stream: cairo_lang_macro_stable::StableTokenStream) -> cairo_lang_macro_stable::StableProcMacroResult { + let token_stream = TokenStream::from_stable(token_stream); let result = #item_name(token_stream); - cairo_lang_macro::stable_abi::StableProcMacroResult::from_proc_macro_result(result) + result.into_stable() } }; TokenStream::from(expanded) diff --git a/plugins/cairo-lang-macro-stable/Cargo.toml b/plugins/cairo-lang-macro-stable/Cargo.toml new file mode 100644 index 000000000..af426d7a8 --- /dev/null +++ b/plugins/cairo-lang-macro-stable/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cairo-lang-macro-stable" +version = "0.0.1" +edition.workspace = true + +authors.workspace = true +categories = ["development-tools"] +description = "Cairo procedural macro stable ABI interface primitives." +homepage.workspace = true +keywords = ["scarb"] +license.workspace = true +readme = "README.md" +repository.workspace = true + +[dependencies] +anyhow.workspace = true +serde.workspace = true +serde_json.workspace = true diff --git a/plugins/cairo-lang-macro-stable/README.md b/plugins/cairo-lang-macro-stable/README.md new file mode 100644 index 000000000..ee1ae3e5d --- /dev/null +++ b/plugins/cairo-lang-macro-stable/README.md @@ -0,0 +1,3 @@ +# cairo-lang-macro-stable + +Stable interface for Scarb procedural macros. diff --git a/plugins/cairo-lang-macro-stable/src/lib.rs b/plugins/cairo-lang-macro-stable/src/lib.rs new file mode 100644 index 000000000..5be4e843d --- /dev/null +++ b/plugins/cairo-lang-macro-stable/src/lib.rs @@ -0,0 +1,51 @@ +use std::ffi::CString; +use std::os::raw::c_char; + +/// Token stream. +/// +/// This struct implements FFI-safe stable ABI. +#[repr(C)] +#[derive(Debug)] +pub struct StableTokenStream(pub *mut c_char); + +#[repr(C)] +#[derive(Debug)] +pub enum StableAuxData { + None, + Some(*mut c_char), +} + +/// Procedural macro result. +/// +/// This struct implements FFI-safe stable ABI. +#[repr(C)] +#[derive(Debug)] +pub enum StableProcMacroResult { + /// Plugin has not taken any action. + Leave, + /// Plugin generated [`StableTokenStream`] replacement. + Replace { + token_stream: StableTokenStream, + aux_data: StableAuxData, + }, + /// Plugin ordered item removal. + Remove, +} + +impl StableTokenStream { + /// Convert to String. + /// + /// # Safety + pub unsafe fn to_string(&self) -> String { + raw_to_string(self.0) + } +} + +unsafe fn raw_to_string(raw: *mut c_char) -> String { + if raw.is_null() { + String::default() + } else { + let cstr = CString::from_raw(raw); + cstr.to_string_lossy().to_string() + } +} diff --git a/plugins/cairo-lang-macro/Cargo.toml b/plugins/cairo-lang-macro/Cargo.toml index 1cb19eb9d..7ca51d792 100644 --- a/plugins/cairo-lang-macro/Cargo.toml +++ b/plugins/cairo-lang-macro/Cargo.toml @@ -15,5 +15,6 @@ repository.workspace = true [dependencies] anyhow.workspace = true cairo-lang-macro-attributes = { path = "../cairo-lang-macro-attributes" } +cairo-lang-macro-stable = { path = "../cairo-lang-macro-stable" } serde.workspace = true serde_json.workspace = true diff --git a/plugins/cairo-lang-macro/src/lib.rs b/plugins/cairo-lang-macro/src/lib.rs index f2a78ccca..2c3546ffa 100644 --- a/plugins/cairo-lang-macro/src/lib.rs +++ b/plugins/cairo-lang-macro/src/lib.rs @@ -1,10 +1,9 @@ use serde_json::Value; +use std::ffi::{c_char, CString}; use std::fmt::Display; pub use cairo_lang_macro_attributes::*; - -#[doc(hidden)] -pub mod stable_abi; +use cairo_lang_macro_stable::{StableAuxData, StableProcMacroResult, StableTokenStream}; #[derive(Debug)] pub enum ProcMacroResult { @@ -48,3 +47,102 @@ impl AuxData { self.0 } } + +impl ProcMacroResult { + /// Convert to FFI-safe representation. + /// + /// # Safety + #[doc(hidden)] + pub fn into_stable(self) -> StableProcMacroResult { + match self { + ProcMacroResult::Leave => StableProcMacroResult::Leave, + ProcMacroResult::Remove => StableProcMacroResult::Remove, + ProcMacroResult::Replace { + token_stream, + aux_data, + } => StableProcMacroResult::Replace { + token_stream: token_stream.into_stable(), + aux_data: AuxData::maybe_into_stable(aux_data), + }, + } + } + + /// Convert to native Rust representation. + /// + /// # Safety + #[doc(hidden)] + pub unsafe fn from_stable(result: StableProcMacroResult) -> Self { + match result { + StableProcMacroResult::Leave => ProcMacroResult::Leave, + StableProcMacroResult::Remove => ProcMacroResult::Remove, + StableProcMacroResult::Replace { + token_stream, + aux_data, + } => ProcMacroResult::Replace { + token_stream: TokenStream::from_stable(token_stream), + aux_data: AuxData::from_stable(aux_data).unwrap(), + }, + } + } +} + +impl TokenStream { + /// Convert to FFI-safe representation. + /// + /// # Safety + #[doc(hidden)] + pub fn into_stable(self) -> StableTokenStream { + let cstr = CString::new(self.0).unwrap(); + StableTokenStream(cstr.into_raw()) + } + + /// Convert to native Rust representation. + /// + /// # Safety + #[doc(hidden)] + pub unsafe fn from_stable(token_stream: StableTokenStream) -> Self { + Self::new(token_stream.to_string()) + } +} + +impl AuxData { + /// Convert to FFI-safe representation. + /// + /// # Safety + pub fn maybe_into_stable(aux_data: Option) -> StableAuxData { + if let Some(aux_data) = aux_data { + aux_data.into_stable() + } else { + StableAuxData::None + } + } + + /// Convert to FFI-safe representation. + /// + /// # Safety + #[doc(hidden)] + pub fn into_stable(self) -> StableAuxData { + let cstr = CString::new(self.0.to_string()).unwrap(); + StableAuxData::Some(cstr.into_raw()) + } + + /// Convert to native Rust representation. + /// + /// # Safety + #[doc(hidden)] + pub unsafe fn from_stable(aux_data: StableAuxData) -> Result, serde_json::Error> { + match aux_data { + StableAuxData::None => Ok(None), + StableAuxData::Some(raw) => Some(Self::try_new(raw_to_string(raw))).transpose(), + } + } +} + +unsafe fn raw_to_string(raw: *mut c_char) -> String { + if raw.is_null() { + String::default() + } else { + let cstr = CString::from_raw(raw); + cstr.to_string_lossy().to_string() + } +} diff --git a/plugins/cairo-lang-macro/src/stable_abi.rs b/plugins/cairo-lang-macro/src/stable_abi.rs deleted file mode 100644 index 273384716..000000000 --- a/plugins/cairo-lang-macro/src/stable_abi.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::{AuxData, ProcMacroResult, TokenStream}; -use std::ffi::CString; -use std::os::raw::c_char; - -/// Token stream. -/// -/// This struct implements FFI-safe stable ABI. -#[repr(C)] -#[derive(Debug)] -pub struct StableTokenStream(pub *mut c_char); - -/// Auxiliary data returned by procedural macro. -/// -/// This struct implements FFI-safe stable ABI. -#[repr(C)] -#[derive(Debug)] -pub enum StableAuxData { - None, - Some(*mut c_char), -} - -/// Procedural macro result. -/// -/// This struct implements FFI-safe stable ABI. -#[repr(C)] -#[derive(Debug)] -pub enum StableProcMacroResult { - /// Plugin has not taken any action. - Leave, - /// Plugin generated [`TokenStream`] replacement. - Replace { - token_stream: StableTokenStream, - aux_data: StableAuxData, - }, - /// Plugin ordered item removal. - Remove, -} - -impl StableTokenStream { - /// Convert to String. - /// - /// # Safety - pub unsafe fn to_string(&self) -> String { - raw_to_string(self.0) - } - - /// Convert to native Rust representation. - /// - /// # Safety - pub unsafe fn into_token_stream(self) -> TokenStream { - TokenStream::new(self.to_string()) - } - - /// Convert to FFI-safe representation. - /// - /// # Safety - pub unsafe fn from_token_stream(token_stream: TokenStream) -> Self { - let cstr = CString::new(token_stream.0).unwrap(); - StableTokenStream(cstr.into_raw()) - } -} - -impl StableAuxData { - pub unsafe fn into_aux_data(self) -> Result, serde_json::Error> { - match self { - Self::None => Ok(None), - Self::Some(raw) => Some(AuxData::try_new(raw_to_string(raw))).transpose(), - } - } - - pub unsafe fn from_aux_data(aux_data: Option) -> Self { - if let Some(aux_data) = aux_data { - let cstr = CString::new(aux_data.0.to_string()).unwrap(); - StableAuxData::Some(cstr.into_raw()) - } else { - StableAuxData::None - } - } -} - -impl StableProcMacroResult { - /// Convert to native Rust representation. - /// - /// # Safety - pub unsafe fn into_proc_macro_result(self) -> ProcMacroResult { - match self { - Self::Leave => ProcMacroResult::Leave, - Self::Remove => ProcMacroResult::Remove, - Self::Replace { - token_stream, - aux_data, - } => ProcMacroResult::Replace { - token_stream: token_stream.into_token_stream(), - aux_data: aux_data.into_aux_data().unwrap(), - }, - } - } - - /// Convert to FFI-safe representation. - /// - /// # Safety - pub unsafe fn from_proc_macro_result(result: ProcMacroResult) -> Self { - match result { - ProcMacroResult::Leave => StableProcMacroResult::Leave, - ProcMacroResult::Remove => StableProcMacroResult::Remove, - ProcMacroResult::Replace { - token_stream, - aux_data, - } => StableProcMacroResult::Replace { - token_stream: StableTokenStream::from_token_stream(token_stream), - aux_data: StableAuxData::from_aux_data(aux_data), - }, - } - } -} - -unsafe fn raw_to_string(raw: *mut c_char) -> String { - if raw.is_null() { - String::default() - } else { - let cstr = CString::from_raw(raw); - cstr.to_string_lossy().to_string() - } -} diff --git a/scarb/Cargo.toml b/scarb/Cargo.toml index bc8dca7d0..77223bcfb 100644 --- a/scarb/Cargo.toml +++ b/scarb/Cargo.toml @@ -54,6 +54,7 @@ redb.workspace = true reqwest.workspace = true scarb-build-metadata = { path = "../utils/scarb-build-metadata" } cairo-lang-macro = { path = "../plugins/cairo-lang-macro" } +cairo-lang-macro-stable = { path = "../plugins/cairo-lang-macro-stable" } scarb-metadata = { path = "../scarb-metadata", default-features = false, features = ["builder"] } scarb-ui = { path = "../utils/scarb-ui" } semver.workspace = true diff --git a/scarb/src/compiler/plugin/proc_macro/ffi.rs b/scarb/src/compiler/plugin/proc_macro/ffi.rs index 9d8411562..746dd7666 100644 --- a/scarb/src/compiler/plugin/proc_macro/ffi.rs +++ b/scarb/src/compiler/plugin/proc_macro/ffi.rs @@ -1,8 +1,8 @@ use crate::core::{Config, Package, PackageId}; use anyhow::{Context, Result}; use cairo_lang_defs::patcher::PatchBuilder; -use cairo_lang_macro::stable_abi::{StableProcMacroResult, StableTokenStream}; use cairo_lang_macro::{ProcMacroResult, TokenStream}; +use cairo_lang_macro_stable::{StableProcMacroResult, StableTokenStream}; use cairo_lang_syntax::node::db::SyntaxGroup; use cairo_lang_syntax::node::{ast, TypedSyntaxNode}; use camino::Utf8PathBuf; @@ -66,9 +66,9 @@ impl ProcMacroInstance { /// Apply expansion to token stream. pub(crate) fn generate_code(&self, token_stream: TokenStream) -> ProcMacroResult { - let ffi_token_stream = unsafe { StableTokenStream::from_token_stream(token_stream) }; + let ffi_token_stream = token_stream.into_stable(); let result = (self.plugin.vtable.expand)(ffi_token_stream); - unsafe { result.into_proc_macro_result() } + unsafe { ProcMacroResult::from_stable(result) } } } diff --git a/scarb/tests/build_cairo_plugin.rs b/scarb/tests/build_cairo_plugin.rs index 57ba4583b..562cdbc7d 100644 --- a/scarb/tests/build_cairo_plugin.rs +++ b/scarb/tests/build_cairo_plugin.rs @@ -49,16 +49,19 @@ impl CairoPluginProjectBuilder { } } -fn cairo_lang_macro_lib_path() -> String { +fn lib_path(lib_name: &str) -> String { let path = fsx::canonicalize( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../plugins/cairo-lang-macro/"), + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../plugins/") + .join(lib_name), ) .unwrap(); serde_json::to_string(&path).unwrap() } fn simple_project(t: &impl PathChild) { - let macro_lib_path = cairo_lang_macro_lib_path(); + let macro_lib_path = lib_path("cairo-lang-macro"); + let macro_stable_lib_path = lib_path("cairo-lang-macro-stable"); CairoPluginProjectBuilder::start() .scarb_project(|b| { b.name("hello") @@ -88,6 +91,7 @@ fn simple_project(t: &impl PathChild) { [dependencies] cairo-lang-macro = {{ path = {macro_lib_path}}} + cairo-lang-macro-stable = {{ path = {macro_stable_lib_path}}} "#}, ) .build(t);