Skip to content

Commit

Permalink
Add aux_data to proc macro result
Browse files Browse the repository at this point in the history
commit-id:89f844e2
  • Loading branch information
maciektr committed Feb 21, 2024
1 parent cb59694 commit 42e0956
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 19 deletions.
20 changes: 19 additions & 1 deletion plugins/cairo-lang-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use serde_json::Value;
use std::fmt::Display;

pub use cairo_lang_macro_attributes::*;
Expand All @@ -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<AuxData>,
},
/// Plugin ordered item removal.
Remove,
}
Expand All @@ -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<T: serde::Serialize>(value: T) -> Result<Self, serde_json::Error> {
Ok(Self(serde_json::to_value(value)?))
}

pub fn to_value(self) -> Value {
self.0
}
}
71 changes: 57 additions & 14 deletions plugins/cairo-lang-macro/src/stable_abi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ProcMacroResult, TokenStream};
use crate::{AuxData, ProcMacroResult, TokenStream};
use std::ffi::CString;
use std::os::raw::c_char;

Expand All @@ -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.
Expand All @@ -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,
}
Expand All @@ -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.
Expand All @@ -52,6 +60,24 @@ impl StableTokenStream {
}
}

impl StableAuxData {
pub unsafe fn into_aux_data(self) -> Result<Option<AuxData>, 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<AuxData>) -> 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.
///
Expand All @@ -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(),
},
}
}

Expand All @@ -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()
}
}
29 changes: 25 additions & 4 deletions scarb/src/compiler/plugin/proc_macro/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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::<Self>().unwrap().0
}
}

impl ProcMacroHostPlugin {
pub fn new(macros: Vec<Arc<ProcMacroInstance>>) -> Self {
Self { macros }
Expand Down Expand Up @@ -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<AuxData> = None;
let mut modified = false;
for input in expansions {
let instance = self
Expand All @@ -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 => {
Expand All @@ -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,
Expand Down

0 comments on commit 42e0956

Please sign in to comment.