Wolfgang Thallergcc16 merge934c3885 months agoHistory
495 lines · 16.2 KB · Rust
RawDownload
//! FFI interface for `rustc_format_parser` use std::alloc::Layout; // what's the plan? Have a function return something that can be constructed into a vector?// or an iterator? trait IntoFFI<T> {    fn into_ffi(self) -> T;} // FIXME: Make an ffi module in a separate file// FIXME: Remember to leak the boxed type somehow// FIXME: How to encode the Option type? As a pointer? Option<T> -> Option<&T> -> *const T could work maybe?pub mod ffi {    use super::IntoFFI;    use std::marker::PhantomData;    use std::mem::MaybeUninit;     #[repr(C)]    pub struct FFIVec<T> {        data: *mut T,        len: usize,        cap: usize    }     impl<T> IntoFFI<FFIVec<T>> for Vec<T> {        fn into_ffi(mut self) -> FFIVec<T> {            let ret = FFIVec {                data: self.as_mut_ptr(),                len: self.len(),                cap: self.capacity()            };            self.leak();            ret        }    }     impl<T> Drop for FFIVec<T> {        fn drop(&mut self) {            unsafe {                Vec::from_raw_parts(self.data, self.len, self.cap);            }        }    }     impl<T> FFIVec<T> {        fn with_vec_ref<R, F: for<'a> FnOnce(&'a Vec<T>) -> R>(            &self, f: F        ) -> R {            let v = unsafe {                Vec::from_raw_parts(self.data, self.len, self.cap)            };            let ret = f(&v);            v.leak();            ret        }         // currently unused        // may be nice to have later, though        #[allow(unused)]        fn with_vec_mut_ref<R, F: for<'a> FnOnce(&'a mut Vec<T>) -> R>(            &mut self, f: F        ) -> R {            let mut v = unsafe {                Vec::from_raw_parts(self.data, self.len, self.cap)            };            let ret = f(&mut v);            self.data = v.as_mut_ptr();            self.len = v.len();            self.cap = v.capacity();            v.leak();            ret        }    }     impl<T> Clone for FFIVec<T>    where        T: Clone    {        fn clone(&self) -> FFIVec<T> {            self.with_vec_ref(|v| v.clone().into_ffi())        }    }     // https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md    #[repr(u8)]    #[derive(Copy, Clone, PartialEq, Eq)]    pub enum FFIOpt<T> {        Some(T),        None    }     impl<T> IntoFFI<FFIOpt<T>> for Option<T> {        fn into_ffi(self) -> FFIOpt<T> {            match self {                Some(v) => FFIOpt::Some(v),                None => FFIOpt::None            }        }    }     // FIXME: We need to ensure we deal with memory properly - whether it's owned by the C++ side or the Rust side    #[derive(Copy, Clone, PartialEq, Eq, Debug)]    #[repr(C)]    pub struct RustHamster<'a> {        ptr: *const u8,        len: usize,        phantom: PhantomData<&'a u8>    }     impl<'a> IntoFFI<RustHamster<'a>> for &'a str {        fn into_ffi(self) -> RustHamster<'a> {            RustHamster {                ptr: self.as_ptr(),                len: self.len(),                phantom: PhantomData,            }        }    }     impl<'a> RustHamster<'a> {        pub fn as_str(&self) -> &'a str {            unsafe {                let slice: &'a [u8] = std::slice::from_raw_parts(self.ptr, self.len);                std::str::from_utf8_unchecked(slice)            }        }    }     // Note: copied from rustc_span    /// Range inside of a `Span` used for diagnostics when we only have access to relative positions.    #[derive(Copy, Clone, PartialEq, Eq, Debug)]    #[repr(C)]    pub struct InnerSpan {        pub start: usize,        pub end: usize,    }     /// The location and before/after width of a character whose width has changed from its source code    /// representation    #[derive(Copy, Clone, PartialEq, Eq)]    #[repr(C)]    pub struct InnerWidthMapping {        /// Index of the character in the source        pub position: usize,        /// The inner width in characters        pub before: usize,        /// The transformed width in characters        pub after: usize,    }     // TODO: Not needed for now?    // /// Whether the input string is a literal. If yes, it contains the inner width mappings.    // #[derive(Clone, PartialEq, Eq)]    // #[repr(C)]    // enum InputStringKind {    //     NotALiteral,    //     Literal {    //         width_mappings: Vec<InnerWidthMapping>,    //     },    // }     // TODO: Not needed for now?    // /// The type of format string that we are parsing.    #[derive(Copy, Clone, Debug, Eq, PartialEq)]    #[repr(C)]    pub enum ParseMode {        /// A normal format string as per `format_args!`.        Format = 0,        /// An inline assembly template string for `asm!`.        InlineAsm,    }     /// A piece is a portion of the format string which represents the next part    /// to emit. These are emitted as a stream by the `Parser` class.    #[derive(Clone)]    #[repr(C)]    pub enum Piece<'a> {        /// A literal string which should directly be emitted        String(RustHamster<'a>),        /// This describes that formatting should process the next argument (as        /// specified inside) for emission.        // do we need a pointer here? we're doing big cloning anyway        NextArgument(Argument<'a>),    }     /// Representation of an argument specification.    #[derive(Clone)]    #[repr(C)]    pub struct Argument<'a> {        /// Where to find this argument        pub position: Position<'a>,        /// The span of the position indicator. Includes any whitespace in implicit        /// positions (`{  }`).        pub position_span: InnerSpan,        /// How to format the argument        pub format: FormatSpec<'a>,    }     /// Specification for the formatting of an argument in the format string.    #[derive(Clone)]    #[repr(C)]    pub struct FormatSpec<'a> {        /// Optionally specified character to fill alignment with.        pub fill: FFIOpt<char>,        /// Span of the optionally specified fill character.        pub fill_span: FFIOpt<InnerSpan>,        /// Optionally specified alignment.        pub align: Alignment,        /// The `+` or `-` flag.        pub sign: FFIOpt<Sign>,        /// The `#` flag.        pub alternate: bool,        /// The `0` flag.        pub zero_pad: bool,        /// The `x` or `X` flag. (Only for `Debug`.)        pub debug_hex: FFIOpt<DebugHex>,        /// The integer precision to use.        pub precision: Count<'a>,        /// The span of the precision formatting flag (for diagnostics).        pub precision_span: FFIOpt<InnerSpan>,        /// The string width requested for the resulting format.        pub width: Count<'a>,        /// The span of the width formatting flag (for diagnostics).        pub width_span: FFIOpt<InnerSpan>,        /// The descriptor string representing the name of the format desired for        /// this argument, this can be empty or any number of characters, although        /// it is required to be one word.        pub ty: RustHamster<'a>,        /// The span of the descriptor string (for diagnostics).        pub ty_span: FFIOpt<InnerSpan>,    }     /// Enum describing where an argument for a format can be located.    #[derive(Copy, Clone, Debug, PartialEq)]    #[repr(C)]    pub enum Position<'a> {        /// The argument is implied to be located at an index        ArgumentImplicitlyIs(usize),        /// The argument is located at a specific index given in the format,        ArgumentIs(usize),        /// The argument has a name.        ArgumentNamed(RustHamster<'a>),    }     /// Enum of alignments which are supported.    #[derive(Copy, Clone, Debug, PartialEq)]    #[repr(C)]    pub enum Alignment {        /// The value will be aligned to the left.        AlignLeft,        /// The value will be aligned to the right.        AlignRight,        /// The value will be aligned in the center.        AlignCenter,        /// The value will take on a default alignment.        AlignUnknown,    }     /// Enum for the sign flags.    #[derive(Copy, Clone, Debug, PartialEq)]    #[repr(C)]    pub enum Sign {        /// The `+` flag.        Plus,        /// The `-` flag.        Minus,    }     /// Enum for the debug hex flags.    #[derive(Copy, Clone, Debug, PartialEq)]    #[repr(C)]    pub enum DebugHex {        /// The `x` flag in `{:x?}`.        Lower,        /// The `X` flag in `{:X?}`.        Upper,    }     /// A count is used for the precision and width parameters of an integer, and    /// can reference either an argument or a literal integer.    #[derive(Copy, Clone, Debug, PartialEq)]    #[repr(C)]    pub enum Count<'a> {        /// The count is specified explicitly.        CountIs(usize),        /// The count is specified by the argument with the given name.        CountIsName(RustHamster<'a>, InnerSpan),        /// The count is specified by the argument at the given index.        CountIsParam(usize),        /// The count is specified by a star (like in `{:.*}`) that refers to the argument at the given index.        CountIsStar(usize),        /// The count is implied and cannot be explicitly specified.        CountImplied,    }     impl<'a> From<generic_format_parser::Piece<'a>> for Piece<'a> {        fn from(old: generic_format_parser::Piece<'a>) -> Self {            match old {                generic_format_parser::Piece::String(x) => Piece::String(x.into_ffi()),                generic_format_parser::Piece::NextArgument(x) => {                    // FIXME: This is problematic - if we do this, then we probably run into the issue that the Box                    // is freed at the end of the call to collect_pieces. if we just .leak() it, then we have                    // a memory leak... should we resend the info back to the Rust lib afterwards to free it?                    // this is definitely the best way - store that pointer in the FFI piece and rebuild the box                    // in a Rust destructor                    let ptr = Box::leak(x);                    let dst = Into::<Argument>::into(*ptr);                     Piece::NextArgument(dst)                }            }        }    }     impl<'a> From<generic_format_parser::Argument<'a>> for Argument<'a> {        fn from(old: generic_format_parser::Argument<'a>) -> Self {            Argument {                position: old.position.into(),                position_span: old.position_span.into(),                format: old.format.into(),            }        }    }     impl<'a> From<generic_format_parser::Position<'a>> for Position<'a> {        fn from(old: generic_format_parser::Position<'a>) -> Self {            match old {                generic_format_parser::Position::ArgumentImplicitlyIs(x) => {                    Position::ArgumentImplicitlyIs(x.into())                }                generic_format_parser::Position::ArgumentIs(x) => Position::ArgumentIs(x.into()),                generic_format_parser::Position::ArgumentNamed(x) => {                    Position::ArgumentNamed(x.into_ffi())                }            }        }    }     impl From<generic_format_parser::InnerSpan> for InnerSpan {        fn from(old: generic_format_parser::InnerSpan) -> Self {            InnerSpan {                start: old.start,                end: old.end,            }        }    }     impl<'a> From<generic_format_parser::FormatSpec<'a>> for FormatSpec<'a> {        fn from(old: generic_format_parser::FormatSpec<'a>) -> Self {            FormatSpec {                fill: old.fill.into_ffi(),                fill_span: old.fill_span.map(Into::into).into_ffi(),                align: old.align.into(),                sign: old.sign.map(Into::into).into_ffi(),                alternate: old.alternate,                zero_pad: old.zero_pad,                debug_hex: old.debug_hex.map(Into::into).into_ffi(),                precision: old.precision.into(),                precision_span: old.precision_span.map(Into::into).into_ffi(),                width: old.width.into(),                width_span: old.width_span.map(Into::into).into_ffi(),                ty: old.ty.into_ffi(),                ty_span: old.ty_span.map(Into::into).into_ffi(),            }        }    }     impl From<generic_format_parser::DebugHex> for DebugHex {        fn from(old: generic_format_parser::DebugHex) -> Self {            match old {                generic_format_parser::DebugHex::Lower => DebugHex::Lower,                generic_format_parser::DebugHex::Upper => DebugHex::Upper,            }        }    }     impl<'a> From<generic_format_parser::Count<'a>> for Count<'a> {        fn from(old: generic_format_parser::Count<'a>) -> Self {            match old {                generic_format_parser::Count::CountIs(x) => Count::CountIs(x),                generic_format_parser::Count::CountIsName(x, y) => Count::CountIsName(x.into_ffi(), y.into()),                generic_format_parser::Count::CountIsParam(x) => Count::CountIsParam(x),                generic_format_parser::Count::CountIsStar(x) => Count::CountIsStar(x),                generic_format_parser::Count::CountImplied => Count::CountImplied,            }        }    }     impl From<generic_format_parser::Sign> for Sign {        fn from(old: generic_format_parser::Sign) -> Self {            match old {                generic_format_parser::Sign::Plus => Sign::Plus,                generic_format_parser::Sign::Minus => Sign::Minus,            }        }    }     impl From<generic_format_parser::Alignment> for Alignment {        fn from(old: generic_format_parser::Alignment) -> Self {            match old {                generic_format_parser::Alignment::AlignLeft => Alignment::AlignLeft,                generic_format_parser::Alignment::AlignRight => Alignment::AlignRight,                generic_format_parser::Alignment::AlignCenter => Alignment::AlignCenter,                generic_format_parser::Alignment::AlignUnknown => Alignment::AlignUnknown,            }        }    }} // FIXME: Rename?pub mod rust {    use crate::ffi::ParseMode;    use generic_format_parser::{Parser, Piece};    pub fn collect_pieces(        input: &str,        style: Option<usize>,        snippet: Option<String>,        append_newline: bool,        parse_mode: ParseMode,    ) -> Vec<Piece<'_>> {        let converted_parse_mode = match parse_mode {            ParseMode::Format => generic_format_parser::ParseMode::Format,            ParseMode::InlineAsm => generic_format_parser::ParseMode::InlineAsm,        };        let parser = Parser::new(input, style, snippet, append_newline, converted_parse_mode);        parser.into_iter().collect()    }} // TODO: Should we instead make an FFIVector struct?type PieceVec<'a> = ffi::FFIVec<ffi::Piece<'a>>; #[no_mangle]pub extern "C" fn collect_pieces<'a>(    input: ffi::RustHamster<'a>,    append_newline: bool,    parse_mode: crate::ffi::ParseMode,) -> PieceVec<'a> {    // FIXME: No unwrap    let pieces: Vec<ffi::Piece<'_>> =        rust::collect_pieces(input.as_str(), None, None, append_newline, parse_mode)            .into_iter()            .map(Into::into)            .collect();     pieces.into_ffi()} #[no_mangle]pub extern "C" fn clone_pieces<'a, 'b>(    piece_vec: &'a PieceVec<'b>) -> PieceVec<'b> {    piece_vec.clone()} // we need Layout::repeat// function signature is a bit different, so call it repeat_xtrait LayoutExt {    fn repeat_x(&self, n: usize) -> Layout;} impl LayoutExt for Layout {    fn repeat_x(&self, n: usize) -> Layout {        let elem = self.pad_to_align();        let total_size = elem.size().checked_mul(n).unwrap();        Layout::from_size_align(total_size, elem.align()).unwrap()    }} #[no_mangle]pub unsafe extern "C" fn rust_ffi_alloc(    count: usize, elem_size: usize, align: usize) -> *mut u8 {    unsafe {        std::alloc::alloc(            Layout::from_size_align_unchecked(elem_size, align)                .repeat_x(count)        )    }} #[no_mangle]pub unsafe extern "C" fn rust_ffi_dealloc(    data: *mut u8, count: usize, elem_size: usize, align: usize) {    unsafe {        std::alloc::dealloc(            data,            Layout::from_size_align_unchecked(elem_size, align)                .repeat_x(count)        )    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t