Skip to content

Commit

Permalink
Remove syn and all other dependencies (#311)
Browse files Browse the repository at this point in the history
Use a codegen tool to inline declarative macros, removing the need for syn at compile-time and for all consumers of this crate.
  • Loading branch information
mmastrac authored Nov 21, 2024
1 parent 16f0898 commit be1345f
Show file tree
Hide file tree
Showing 12 changed files with 2,146 additions and 288 deletions.
41 changes: 28 additions & 13 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[workspace]
members = [ "ctor", "dtor", "tests" ]
members = [ "ctor", "codegen", "dtor", "tests" ]
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ Module initialization/teardown functions for Rust (like `__attribute__((construc
This library currently requires **Rust > 1.31.0** at a minimum for the
procedural macro support.

Idea inspired by [this code](https://github.com/neon-bindings/neon/blob/2277e943a619579c144c1da543874f4a7ec39879/src/lib.rs#L42) in the Neon project.
## Zero Dependency

As of `ctor 0.3.0+`, `ctor` has no dependencies. The proc macro in this crate
inlines a helper declarative macro that does the majority of the work.

## Support

Expand Down Expand Up @@ -108,3 +111,7 @@ The `#[dtor]` macro effectively creates a constructor that calls `libc::atexit`
libc::atexit(dtor);
}
```

## Inspiration

Idea inspired by [this code](https://github.com/neon-bindings/neon/blob/2277e943a619579c144c1da543874f4a7ec39879/src/lib.rs#L42) in the Neon project.
13 changes: 13 additions & 0 deletions codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "ctor-codegen"
version = "0.1.0"
authors = ["Matt Mastracci <matthew@mastracci.com>"]
edition = "2018"
publish = false

[dependencies]
libc-print = "0.1.20"
syn = "2"
quote = "1"
proc-macro2 = "1"
paste = "1"
4 changes: 4 additions & 0 deletions codegen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# ctor codegen

This project does the codegen to ensure that ctor builds without dependencies. We use
the `quote!` macro to generate a `TokenStream`, and then dump that into a source file.
190 changes: 190 additions & 0 deletions codegen/src/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
crate::declare_macro!(
macro_rules! ctor_raw {
($used:meta $($block:tt)+) => {
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "illumos",
target_os = "haiku",
target_vendor = "apple",
windows)))]
compile_error!("#[ctor] is not supported on the current target");

#[$used]
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".init_array")]
#[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
#[cfg_attr(target_os = "netbsd", link_section = ".init_array")]
#[cfg_attr(target_os = "openbsd", link_section = ".init_array")]
#[cfg_attr(target_os = "dragonfly", link_section = ".init_array")]
#[cfg_attr(target_os = "illumos", link_section = ".init_array")]
#[cfg_attr(target_os = "haiku", link_section = ".init_array")]
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(windows, link_section = ".CRT$XCU")]
$($block)+
}
}
);

crate::declare_macro!(
macro_rules! ctor_impl {
(
fn
macros=$macros:ident
name=$name:ident
used=$used:meta
$(extra={ $($extra:item)+ })?
item={
$(
#[$meta:meta]
)*
$vis:vis $(unsafe)? fn $ident:ident () $(-> $ret:ty)? $block:block
}
) => {
$(#[$meta])*
#[allow(unused)]
$vis unsafe extern "C" fn $ident() $block

#[doc(hidden)]
#[allow(unused, non_snake_case)]
mod $name {
super::$macros::ctor_raw!($used
#[allow(non_upper_case_globals, non_snake_case)]
#[doc(hidden)]
static $name: unsafe extern "C" fn() -> usize =
{
#[allow(non_snake_case)]
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
unsafe extern "C" fn $name() -> usize { super::$ident(); 0 }

$name
};
);

$( $($extra)+ )?
}
};

(
static
macros=$macros:ident
name=$name:ident
used=$used:meta
item={
$(
#[$meta:meta]
)*
$vis:vis static $ident:ident: $ty:ty = $expr:expr;
}
) => {

$(#[$meta])*
$vis static $ident: $name::Static<$ty> = $name::Static::<$ty> {
_storage: ::std::cell::UnsafeCell::new(None)
};

impl ::core::ops::Deref for $name::Static<$ty> {
type Target = $ty;
fn deref(&self) -> &'static $ty {
unsafe {
let ptr = self._storage.get();
let val = (&*ptr).as_ref().unwrap();
val
}
}
}

$macros::ctor_impl!(
fn
macros=$macros
name=$name
used=$used
extra={
#[doc(hidden)]
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct Static<T> {
pub _storage: ::std::cell::UnsafeCell<::std::option::Option<T>>
}

unsafe impl <T> std::marker::Sync for Static<T> {}
}
item={
#[allow(non_snake_case)]
fn $name() {
let val = Some($expr);
// Only write the value to `storage_ident` on startup
unsafe {
let ptr = $ident._storage.get();
::std::ptr::write(ptr, val);
}
}
}
);
};

(
dtor
macros=$macros:ident
name=$name:ident
used=$used:meta
item={
$(
#[$meta:meta]
)*
$vis:vis $(unsafe)? fn $ident:ident () $(-> $ret:ty)? $block:block
}
) => {
$(#[$meta])*
#[allow(unused)]
$vis unsafe extern "C" fn $ident() $block

#[doc(hidden)]
#[allow(unused, non_snake_case)]
mod $name {
super::$macros::ctor_raw!($used
#[allow(non_upper_case_globals, non_snake_case)]
#[doc(hidden)]
static $name: unsafe extern "C" fn() -> usize =
{
#[allow(non_snake_case)]
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.startup")]
unsafe extern "C" fn $name() -> usize { do_atexit(__dtor); 0 }

$name
};
);

#[cfg(not(target_vendor = "apple"))]
#[cfg_attr(any(target_os = "linux", target_os = "android"), link_section = ".text.exit")]
unsafe extern "C" fn __dtor() { super::$ident() }
#[cfg(target_vendor = "apple")]
unsafe extern "C" fn __dtor(_: *const u8) { super::$ident() }

#[cfg(not(target_vendor = "apple"))]
#[inline(always)]
pub(super) unsafe fn do_atexit(cb: unsafe extern fn()) {
extern "C" {
fn atexit(cb: unsafe extern fn());
}
atexit(cb);
}

// For platforms that have __cxa_atexit, we register the dtor as scoped to dso_handle
#[cfg(target_vendor = "apple")]
#[inline(always)]
pub(super) unsafe fn do_atexit(cb: unsafe extern fn(_: *const u8)) {
extern "C" {
static __dso_handle: *const u8;
fn __cxa_atexit(cb: unsafe extern fn(_: *const u8), arg: *const u8, dso_handle: *const u8);
}
__cxa_atexit(cb, core::ptr::null(), __dso_handle);
}

}
};
}
);
Loading

0 comments on commit be1345f

Please sign in to comment.