diff --git a/plugins/cairo-lang-macro/src/lib.rs b/plugins/cairo-lang-macro/src/lib.rs index bc268dd43..f2a78ccca 100644 --- a/plugins/cairo-lang-macro/src/lib.rs +++ b/plugins/cairo-lang-macro/src/lib.rs @@ -1,3 +1,4 @@ +use serde_json::Value; use std::fmt::Display; pub use cairo_lang_macro_attributes::*; @@ -10,7 +11,10 @@ pub enum ProcMacroResult { /// Plugin has not taken any action. Leave, /// Plugin generated [`TokenStream`] replacement. - Replace(TokenStream), + Replace { + token_stream: TokenStream, + aux_data: Option, + }, /// Plugin ordered item removal. Remove, } @@ -30,3 +34,17 @@ impl Display for TokenStream { write!(f, "{}", self.0) } } + +/// Auxiliary data returned by procedural macro. +#[derive(Debug)] +pub struct AuxData(Value); + +impl AuxData { + pub fn try_new(value: T) -> Result { + Ok(Self(serde_json::to_value(value)?)) + } + + pub fn to_value(self) -> Value { + self.0 + } +} diff --git a/plugins/cairo-lang-macro/src/stable_abi.rs b/plugins/cairo-lang-macro/src/stable_abi.rs index 55748b4f4..273384716 100644 --- a/plugins/cairo-lang-macro/src/stable_abi.rs +++ b/plugins/cairo-lang-macro/src/stable_abi.rs @@ -1,4 +1,4 @@ -use crate::{ProcMacroResult, TokenStream}; +use crate::{AuxData, ProcMacroResult, TokenStream}; use std::ffi::CString; use std::os::raw::c_char; @@ -9,6 +9,16 @@ use std::os::raw::c_char; #[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. @@ -18,7 +28,10 @@ pub enum StableProcMacroResult { /// Plugin has not taken any action. Leave, /// Plugin generated [`TokenStream`] replacement. - Replace(StableTokenStream), + Replace { + token_stream: StableTokenStream, + aux_data: StableAuxData, + }, /// Plugin ordered item removal. Remove, } @@ -28,12 +41,7 @@ impl StableTokenStream { /// /// # Safety pub unsafe fn to_string(&self) -> String { - if self.0.is_null() { - String::default() - } else { - let cstr = CString::from_raw(self.0); - cstr.to_string_lossy().to_string() - } + raw_to_string(self.0) } /// Convert to native Rust representation. @@ -52,6 +60,24 @@ impl StableTokenStream { } } +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. /// @@ -60,9 +86,13 @@ impl StableProcMacroResult { match self { Self::Leave => ProcMacroResult::Leave, Self::Remove => ProcMacroResult::Remove, - Self::Replace(token_stream) => { - ProcMacroResult::Replace(token_stream.into_token_stream()) - } + Self::Replace { + token_stream, + aux_data, + } => ProcMacroResult::Replace { + token_stream: token_stream.into_token_stream(), + aux_data: aux_data.into_aux_data().unwrap(), + }, } } @@ -73,9 +103,22 @@ impl StableProcMacroResult { match result { ProcMacroResult::Leave => StableProcMacroResult::Leave, ProcMacroResult::Remove => StableProcMacroResult::Remove, - ProcMacroResult::Replace(token_stream) => { - StableProcMacroResult::Replace(StableTokenStream::from_token_stream(token_stream)) - } + 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/src/compiler/plugin/proc_macro/host.rs b/scarb/src/compiler/plugin/proc_macro/host.rs index e7b7835db..6e36db66a 100644 --- a/scarb/src/compiler/plugin/proc_macro/host.rs +++ b/scarb/src/compiler/plugin/proc_macro/host.rs @@ -2,15 +2,17 @@ use crate::compiler::plugin::proc_macro::{FromItemAst, ProcMacroInstance}; use crate::core::{Config, Package, PackageId}; use anyhow::Result; use cairo_lang_defs::plugin::{ - MacroPlugin, MacroPluginMetadata, PluginGeneratedFile, PluginResult, + DynGeneratedFileAuxData, GeneratedFileAuxData, MacroPlugin, MacroPluginMetadata, + PluginGeneratedFile, PluginResult, }; -use cairo_lang_macro::{ProcMacroResult, TokenStream}; +use cairo_lang_macro::{AuxData, ProcMacroResult, TokenStream}; use cairo_lang_semantic::plugin::PluginSuite; use cairo_lang_syntax::attribute::structured::AttributeListStructurize; use cairo_lang_syntax::node::ast; use cairo_lang_syntax::node::db::SyntaxGroup; use itertools::Itertools; use smol_str::SmolStr; +use std::any::Any; use std::sync::Arc; /// A Cairo compiler plugin controlling the procedural macro execution. @@ -42,6 +44,19 @@ pub struct ProcMacroInput { pub macro_package_id: PackageId, } +#[derive(Debug)] +pub struct ProcMacroAuxData(serde_json::Value); + +impl GeneratedFileAuxData for ProcMacroAuxData { + fn as_any(&self) -> &dyn Any { + self + } + + fn eq(&self, other: &dyn GeneratedFileAuxData) -> bool { + self.0 == other.as_any().downcast_ref::().unwrap().0 + } +} + impl ProcMacroHostPlugin { pub fn new(macros: Vec>) -> Self { Self { macros } @@ -122,6 +137,7 @@ impl MacroPlugin for ProcMacroHostPlugin { .chain(self.handle_derive(db, item_ast.clone())); let mut token_stream = TokenStream::from_item_ast(db, item_ast); + let mut aux_data: Option = None; let mut modified = false; for input in expansions { let instance = self @@ -130,8 +146,12 @@ impl MacroPlugin for ProcMacroHostPlugin { .find(|m| m.package_id() == input.macro_package_id) .expect("procedural macro must be registered in proc macro host"); match instance.generate_code(token_stream.clone()) { - ProcMacroResult::Replace(new_token_stream) => { + ProcMacroResult::Replace { + token_stream: new_token_stream, + aux_data: new_aux_data, + } => { token_stream = new_token_stream; + aux_data = new_aux_data; modified = true; } ProcMacroResult::Remove => { @@ -150,7 +170,8 @@ impl MacroPlugin for ProcMacroHostPlugin { name: "proc_macro".into(), content: token_stream.to_string(), code_mappings: Default::default(), - aux_data: Default::default(), + aux_data: aux_data + .map(|ad| DynGeneratedFileAuxData::new(ProcMacroAuxData(ad.to_value()))), }), diagnostics: Vec::new(), remove_original_item: true,