Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use Box::{leak,from_raw} to create self-reference for Pattern parser
Browse files Browse the repository at this point in the history
SpriteOvO committed Mar 21, 2024
1 parent 62d2abf commit cb21cb0
Showing 2 changed files with 28 additions and 25 deletions.
1 change: 0 additions & 1 deletion spdlog-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -17,6 +17,5 @@ proc-macro = true
nom = "7.1.1"
proc-macro2 = "1.0.47"
quote = "1.0.21"
self_cell = "1.0.1"
spdlog-internal = { version = "=0.1.0", path = "../spdlog-internal" }
syn = { version = "2.0.38", features = ["full"] }
52 changes: 28 additions & 24 deletions spdlog-macros/src/pattern/mod.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ mod synthesis;

use proc_macro2::{Span, TokenStream};
use quote::quote;
use self_cell::self_cell;
use spdlog_internal::pattern_parser::{
check_custom_pattern_names, parse::Template, PatternRegistry, Result,
};
@@ -71,26 +70,18 @@ pub fn runtime_pattern_impl(runtime_pattern: RuntimePattern) -> Result<TokenStre
pub struct Pattern {
/// The template string included in the pattern.
template: TemplateSelfRef,
template: Option<(&'static String, Template<'static>)>,
/// Any user-provided pattern-to-formatter mapping.
custom_patterns: CustomPatterns,
}

self_cell! {
pub struct TemplateSelfRef {
owner: String,
#[covariant]
dependent: Template,
}
}

impl Pattern {
fn custom_patterns(&self) -> impl IntoIterator<Item = &(Ident, Path)> {
self.custom_patterns.0.iter()
}

fn template(&self) -> &Template {
self.template.borrow_dependent()
&self.template.as_ref().unwrap().1
}
}

@@ -100,20 +91,33 @@ impl Parse for Pattern {
input.parse::<Option<Token![,]>>()?;
let custom_patterns = input.parse()?;

let ret = Pattern {
template: TemplateSelfRef::try_new(template_lit.value(), |template_str| {
Template::parse(template_str).map_err(|err| {
syn::Error::new(
// TODO: Maybe we can make a subspan for the literal for a better error
// message
template_lit.span(),
err,
)
})
})?,
// Struct `Template` have almost no way of owning a `String`, we have to store
// `template_lit` somewhere. Here we use `Box::leak` + `Box::from_raw` to create
// a simple self-reference.
let template_lit_leaked = Box::leak(Box::new(template_lit.value()));

let template = Template::parse(template_lit_leaked).map_err(|err| {
syn::Error::new(
// TODO: Maybe we can make a subspan for the literal for a better error message
template_lit.span(),
err,
)
})?;

Ok(Pattern {
template: Some((template_lit_leaked, template)),
custom_patterns,
};
Ok(ret)
})
}
}

impl Drop for Pattern {
fn drop(&mut self) {
let (template_lit_leaked, template) = self.template.take().unwrap();
// Drop the user of the leaked data first.
drop(template);
// Restore the ownership of the leaked `String` and then drop it.
drop(unsafe { Box::from_raw(template_lit_leaked as *const String as *mut String) });
}
}

0 comments on commit cb21cb0

Please sign in to comment.