Skip to content

Commit

Permalink
Implement loading shared libraries for proc macro plugins
Browse files Browse the repository at this point in the history
commit-id:a3155bbf
  • Loading branch information
maciektr committed Jan 30, 2024
1 parent 87eb720 commit e58ce04
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 53 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ xshell = "0.2"
xxhash-rust = { version = "0.8", features = ["xxh3"] }
zip = { version = "0.6", default-features = false, features = ["deflate"] }
zstd = "0.13"
libloading = "0.8.1"

[profile.release]
lto = true
Expand Down
2 changes: 2 additions & 0 deletions scarb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ xxhash-rust.workspace = true
zip.workspace = true
zstd.workspace = true
scarb-proc-macro-interface = { path = "../utils/scarb-proc-macro-interface" }
libloading.workspace = true
libc.workspace = true

[target.'cfg(not(target_os = "linux"))'.dependencies]
reqwest = { workspace = true, default-features = true}
Expand Down
3 changes: 2 additions & 1 deletion scarb/src/compiler/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use smol_str::SmolStr;
use std::sync::Arc;
use tracing::trace;

use crate::compiler::plugin::{CairoPlugin, ProcMacroHostPlugin};
use crate::compiler::plugin::proc_macro::ProcMacroHostPlugin;
use crate::compiler::plugin::CairoPlugin;
use crate::compiler::{CompilationUnit, CompilationUnitComponent};
use crate::core::Workspace;
use crate::DEFAULT_MODULE_MAIN_FILE;
Expand Down
3 changes: 1 addition & 2 deletions scarb/src/compiler/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ use crate::core::PackageId;
use self::builtin::{BuiltinStarkNetPlugin, BuiltinTestPlugin};

pub mod builtin;
pub(crate) mod proc_macro_host;
pub use proc_macro_host::ProcMacroHostPlugin;
pub mod proc_macro;

pub trait CairoPlugin: Sync {
fn id(&self) -> PackageId;
Expand Down
135 changes: 135 additions & 0 deletions scarb/src/compiler/plugin/proc_macro/ffi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
use crate::core::Package;
use cairo_lang_defs::patcher::PatchBuilder;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{ast, TypedSyntaxNode};
use camino::Utf8PathBuf;
use libloading::{Library, Symbol};
use scarb_proc_macro_interface::shared::{FfiProcMacroResult, FfiTokenStream};
use std::ffi::CString;
use std::fmt::Debug;

#[cfg(not(windows))]
use libloading::os::unix::Symbol as RawSymbol;
#[cfg(windows)]
use libloading::os::windows::Symbol as RawSymbol;

#[derive(Debug, Default, Clone)]
pub struct TokenStream(String);

impl TokenStream {
/// Convert to struct with stable ABI, `FfiTokenStream`.
///
pub fn to_ffi(&self) -> FfiTokenStream {
let cstring = CString::new(self.0.clone()).expect("CString::new failed");
FfiTokenStream(cstring.into_raw())
}

/// Convert from struct with stable ABI, `FfiTokenStream`.
///
/// # Safety
pub unsafe fn from_ffi(token_stream: FfiTokenStream) -> Self {
Self(token_stream.to_string())
}
}

impl TokenStream {
pub fn from_item_ast(db: &dyn SyntaxGroup, item_ast: ast::ModuleItem) -> Self {
let mut builder = PatchBuilder::new(db);
builder.add_node(item_ast.as_syntax_node());
let cairo = builder.code.clone();
Self(cairo)
}

pub fn collect(self) -> String {
self.0
}
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum ProcMacroResult {
/// Plugin has not taken any action.
Leave,
/// Plugin generated TokenStream replacement.
Replace(TokenStream),
/// Plugin ordered item removal.
Remove,
}

impl ProcMacroResult {
/// Convert from struct with stable ABI, `FfiProcMacroResult`.
///
/// # Safety
pub unsafe fn from_ffi(ffi_result: FfiProcMacroResult) -> Self {
match ffi_result {
FfiProcMacroResult::Leave => Self::Leave,
FfiProcMacroResult::Remove => Self::Remove,
FfiProcMacroResult::Replace(token_stream) => {
Self::Replace(TokenStream::from_ffi(token_stream))
}
}
}
}

#[non_exhaustive]
pub struct ProcMacroInstance {
plugin: Plugin,
}

impl Debug for ProcMacroInstance {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Ok(())
}
}

fn shared_lib_path(package: &Package) -> Utf8PathBuf {
let lib_name = format!("{}.dylib", package.id.name);
package.root().join("target").join("release").join(lib_name)
}

impl ProcMacroInstance {
/// Load shared library
pub fn new(package: Package) -> Self {
let plugin = unsafe { Plugin::new(shared_lib_path(&package)) };
Self { plugin }
}

/// Apply expansion to token stream.
pub(crate) fn generate_code(&self, token_stream: TokenStream) -> ProcMacroResult {
let ffi_token_stream = token_stream.to_ffi();
let result = (self.plugin.vtable.expand)(ffi_token_stream);
unsafe { ProcMacroResult::from_ffi(result) }
}
}

type ExpandCode = extern "C" fn(FfiTokenStream) -> FfiProcMacroResult;

struct VTableV0 {
expand: RawSymbol<ExpandCode>,
}

impl VTableV0 {
unsafe fn new(library: &Library) -> VTableV0 {
println!("Loading plugin API version 0...");

let expand: Symbol<'_, ExpandCode> = library.get(b"expand\0").unwrap();
let expand = expand.into_raw();

VTableV0 { expand }
}
}

struct Plugin {
#[allow(dead_code)]
library: Library,
vtable: VTableV0,
}

impl Plugin {
unsafe fn new(library_name: Utf8PathBuf) -> Plugin {
let library = Library::new(library_name).unwrap();
let vtable = VTableV0::new(&library);

Plugin { library, vtable }
}
}
Original file line number Diff line number Diff line change
@@ -1,68 +1,24 @@
use crate::compiler::plugin::proc_macro::{ProcMacroInstance, ProcMacroResult, TokenStream};
use crate::compiler::plugin::{CairoPlugin, CairoPluginInstance};
use crate::core::{Package, PackageId, PackageName, SourceId};
use crate::internal::to_version::ToVersion;
use anyhow::Result;
use cairo_lang_defs::patcher::PatchBuilder;
use cairo_lang_defs::plugin::{
MacroPlugin, MacroPluginMetadata, PluginGeneratedFile, PluginResult,
};
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 cairo_lang_syntax::node::{ast, TypedSyntaxNode};
use itertools::Itertools;
use smol_str::SmolStr;
use std::collections::HashMap;
use std::sync::Arc;
use typed_builder::TypedBuilder;

#[derive(Debug, Default, Clone)]
pub struct TokenStream(String);

impl TokenStream {
pub fn from_item_ast(db: &dyn SyntaxGroup, item_ast: ast::ModuleItem) -> Self {
let mut builder = PatchBuilder::new(db);
builder.add_node(item_ast.as_syntax_node());
let cairo = builder.code.clone();
Self(cairo)
}

pub fn collect(self) -> String {
self.0
}
}

#[derive(Debug)]
#[allow(dead_code)]
pub enum ProcMacroResult {
/// Plugin has not taken any action.
Leave,
/// Plugin generated TokenStream replacement.
Replace(TokenStream),
/// Plugin ordered item removal.
Remove,
}

#[derive(Debug, Clone)]
pub struct ProcMacroInstance {}

impl ProcMacroInstance {
pub fn new(_package: Package) -> Self {
// Load shared library
// TODO(maciektr): Implement
Self {}
}

fn generate_code(&self, _token_stream: TokenStream) -> ProcMacroResult {
// Apply expansion to token stream.
// TODO(maciektr): Implement
ProcMacroResult::Leave
}
}

#[derive(Debug, TypedBuilder)]
pub struct ProcMacroHost {
macros: HashMap<SmolStr, Box<ProcMacroInstance>>,
macros: HashMap<SmolStr, Arc<ProcMacroInstance>>,
}

pub type ProcMacroId = SmolStr;
Expand Down Expand Up @@ -191,7 +147,7 @@ impl MacroPlugin for ProcMacroHost {

#[derive(Default)]
pub struct ProcMacroHostPlugin {
macros: HashMap<SmolStr, Box<ProcMacroInstance>>,
macros: HashMap<SmolStr, Arc<ProcMacroInstance>>,
}

impl ProcMacroHostPlugin {
Expand All @@ -208,7 +164,7 @@ impl ProcMacroHostPlugin {
// Register instance in hash map
let name = package.id.name.to_smol_str();
let instance = ProcMacroInstance::new(package);
self.macros.insert(name, Box::new(instance));
self.macros.insert(name, Arc::new(instance));
}
}

Expand All @@ -227,7 +183,7 @@ impl CairoPlugin for ProcMacroHostPlugin {

#[derive(TypedBuilder)]
pub struct ProcMacroHostPluginInstance {
macros: HashMap<SmolStr, Box<ProcMacroInstance>>,
macros: HashMap<SmolStr, Arc<ProcMacroInstance>>,
}

impl ProcMacroHostPluginInstance {
Expand Down
5 changes: 5 additions & 0 deletions scarb/src/compiler/plugin/proc_macro/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod ffi;
mod host;

pub use ffi::*;
pub use host::*;

0 comments on commit e58ce04

Please sign in to comment.