From 0748450e838b3bde08d711322b20a7c39e3a341c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 10 May 2021 21:27:43 +0100 Subject: [PATCH 1/9] rust: Rename libmodule to libmacros Renaming to libmacros allows us to potentially have more procedural macros in the future without introducing additional crates. Signed-off-by: Gary Guo --- rust/Makefile | 22 +++--- rust/kernel/module_param.rs | 4 +- rust/kernel/prelude.rs | 2 +- rust/macros/lib.rs | 126 ++++++++++++++++++++++++++++++ rust/{ => macros}/module.rs | 114 +-------------------------- scripts/generate_rust_analyzer.py | 8 +- 6 files changed, 145 insertions(+), 131 deletions(-) create mode 100644 rust/macros/lib.rs rename rust/{ => macros}/module.rs (86%) diff --git a/rust/Makefile b/rust/Makefile index 85c1904b674b78..4a03693bdc9e22 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_RUST) += core.o compiler_builtins.o helpers.o extra-$(CONFIG_RUST) += exports_core_generated.h -extra-$(CONFIG_RUST) += libmodule.so +extra-$(CONFIG_RUST) += libmacros.so extra-$(CONFIG_RUST) += bindings_generated.rs obj-$(CONFIG_RUST) += alloc.o kernel.o @@ -35,11 +35,11 @@ quiet_cmd_rustdoc = RUSTDOC $< --output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \ -Fmissing-docs @$(objtree)/include/generated/rustc_cfg $< -rustdoc: rustdoc-module rustdoc-compiler_builtins rustdoc-kernel +rustdoc: rustdoc-macros rustdoc-compiler_builtins rustdoc-kernel -rustdoc-module: private rustdoc_target_flags = --crate-type proc-macro \ +rustdoc-macros: private rustdoc_target_flags = --crate-type proc-macro \ --extern proc_macro -rustdoc-module: $(srctree)/rust/module.rs FORCE +rustdoc-macros: $(srctree)/rust/macros/lib.rs FORCE $(call if_changed,rustdoc_host) rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE @@ -47,9 +47,9 @@ rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE rustdoc-kernel: private rustdoc_target_flags = --extern alloc \ --extern build_error \ - --extern module=$(objtree)/rust/libmodule.so -rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \ - $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE + --extern macros=$(objtree)/rust/libmacros.so +rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-macros \ + $(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE $(call if_changed,rustdoc) ifdef CONFIG_CC_IS_CLANG @@ -126,7 +126,7 @@ quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@ mv $(objtree)/rust/$(patsubst lib%.so,%,$(notdir $@)).d $(depfile); \ sed -i '/^\#/d' $(depfile) -$(objtree)/rust/libmodule.so: $(srctree)/rust/module.rs FORCE +$(objtree)/rust/libmacros.so: $(srctree)/rust/macros/lib.rs FORCE $(call if_changed_dep,rustc_procmacro) quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@ @@ -166,11 +166,11 @@ $(objtree)/rust/build_error.o: $(srctree)/rust/build_error.rs \ $(objtree)/rust/compiler_builtins.o FORCE $(call if_changed_dep,rustc_library) -# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935 +# ICE on `--extern macros`: https://github.com/rust-lang/rust/issues/56935 $(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \ --extern build_error \ - --extern module=$(objtree)/rust/libmodule.so + --extern macros=$(objtree)/rust/libmacros.so $(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \ $(objtree)/rust/build_error.o \ - $(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE + $(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE $(call if_changed_dep,rustc_library) diff --git a/rust/kernel/module_param.rs b/rust/kernel/module_param.rs index c70f61367347f3..fc6a6b01c588db 100644 --- a/rust/kernel/module_param.rs +++ b/rust/kernel/module_param.rs @@ -52,7 +52,7 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized { /// Get the current value of the parameter for use in the kernel module. /// /// This function should not be used directly. Instead use the wrapper - /// `read` which will be generated by [`module::module`]. + /// `read` which will be generated by [`macros::module`]. fn value(&self) -> &Self::Value; /// Set the module parameter from a string. @@ -428,7 +428,7 @@ impl ModuleParam /// A C-style string parameter. /// /// The Rust version of the [`charp`] parameter. This type is meant to be -/// used by the [`module::module`] macro, not handled directly. Instead use the +/// used by the [`macros::module`] macro, not handled directly. Instead use the /// `read` method generated by that macro. /// /// [`charp`]: ../../../include/linux/moduleparam.h diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs index fad94708fa6f2d..96e39bbc8063f2 100644 --- a/rust/kernel/prelude.rs +++ b/rust/kernel/prelude.rs @@ -15,7 +15,7 @@ pub use alloc::{borrow::ToOwned, string::String}; pub use super::build_assert; -pub use module::{module, module_misc_device}; +pub use macros::{module, module_misc_device}; pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice, pr_warn}; diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs new file mode 100644 index 00000000000000..a0c882dc8aa81d --- /dev/null +++ b/rust/macros/lib.rs @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Crate for all kernel procedural macros. +mod module; + +use proc_macro::TokenStream; + +/// Declares a kernel module. +/// +/// The `type` argument should be a type which implements the [`KernelModule`] +/// trait. Also accepts various forms of kernel metadata. +/// +/// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) +/// +/// [`KernelModule`]: ../kernel/trait.KernelModule.html +/// +/// # Examples +/// +/// ```rust,no_run +/// use kernel::prelude::*; +/// +/// module!{ +/// type: MyKernelModule, +/// name: b"my_kernel_module", +/// author: b"Rust for Linux Contributors", +/// description: b"My very own kernel module!", +/// license: b"GPL v2", +/// params: { +/// my_i32: i32 { +/// default: 42, +/// permissions: 0o000, +/// description: b"Example of i32", +/// }, +/// writeable_i32: i32 { +/// default: 42, +/// permissions: 0o644, +/// description: b"Example of i32", +/// }, +/// }, +/// } +/// +/// struct MyKernelModule; +/// +/// impl KernelModule for MyKernelModule { +/// fn init() -> Result { +/// // If the parameter is writeable, then the kparam lock must be +/// // taken to read the parameter: +/// { +/// let lock = THIS_MODULE.kernel_param_lock(); +/// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock)); +/// } +/// // If the parameter is read only, it can be read without locking +/// // the kernel parameters: +/// pr_info!("i32 param is: {}\n", my_i32.read()); +/// Ok(MyKernelModule) +/// } +/// } +/// ``` +/// +/// # Supported argument types +/// - `type`: type which implements the [`KernelModule`] trait (required). +/// - `name`: byte array of the name of the kernel module (required). +/// - `author`: byte array of the author of the kernel module. +/// - `description`: byte array of the description of the kernel module. +/// - `license`: byte array of the license of the kernel module (required). +/// - `alias`: byte array of alias name of the kernel module. +/// - `alias_rtnl_link`: byte array of the `rtnl_link_alias` of the kernel module (mutually exclusive with `alias`). +/// - `params`: parameters for the kernel module, as described below. +/// +/// # Supported parameter types +/// +/// - `bool`: Corresponds to C `bool` param type. +/// - `i8`: No equivalent C param type. +/// - `u8`: Corresponds to C `char` param type. +/// - `i16`: Corresponds to C `short` param type. +/// - `u16`: Corresponds to C `ushort` param type. +/// - `i32`: Corresponds to C `int` param type. +/// - `u32`: Corresponds to C `uint` param type. +/// - `i64`: No equivalent C param type. +/// - `u64`: Corresponds to C `ullong` param type. +/// - `isize`: No equivalent C param type. +/// - `usize`: No equivalent C param type. +/// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice. +/// - `ArrayParam`: Corresponds to C parameters created using `module_param_array`. An array +/// of `T`'s of length at **most** `N`. +/// +/// `invbool` is unsupported: it was only ever used in a few modules. +/// Consider using a `bool` and inverting the logic instead. +#[proc_macro] +pub fn module(ts: TokenStream) -> TokenStream { + module::module(ts) +} + +/// Declares a kernel module that exposes a single misc device. +/// +/// The `type` argument should be a type which implements the [`FileOpener`] trait. Also accepts +/// various forms of kernel metadata. +/// +/// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) +/// +/// [`FileOpener`]: ../kernel/file_operations/trait.FileOpener.html +/// +/// # Examples +/// +/// ```rust,no_run +/// use kernel::prelude::*; +/// +/// module_misc_device! { +/// type: MyFile, +/// name: b"my_miscdev_kernel_module", +/// author: b"Rust for Linux Contributors", +/// description: b"My very own misc device kernel module!", +/// license: b"GPL v2", +/// } +/// +/// #[derive(Default)] +/// struct MyFile; +/// +/// impl kernel::file_operations::FileOperations for MyFile { +/// kernel::declare_file_operations!(); +/// } +/// ``` +#[proc_macro] +pub fn module_misc_device(ts: TokenStream) -> TokenStream { + module::module_misc_device(ts) +} diff --git a/rust/module.rs b/rust/macros/module.rs similarity index 86% rename from rust/module.rs rename to rust/macros/module.rs index 9a7009644d3cd7..0a155b8d4c9f7f 100644 --- a/rust/module.rs +++ b/rust/macros/module.rs @@ -1,9 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -//! Proc macro crate implementing the [`module!`] magic. -//! -//! C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h) - #![deny(clippy::complexity)] #![deny(clippy::correctness)] #![deny(clippy::perf)] @@ -399,86 +395,6 @@ impl ModuleInfo { } } -/// Declares a kernel module. -/// -/// The `type` argument should be a type which implements the [`KernelModule`] -/// trait. Also accepts various forms of kernel metadata. -/// -/// [`KernelModule`]: ../kernel/trait.KernelModule.html -/// -/// # Examples -/// -/// ```rust,no_run -/// use kernel::prelude::*; -/// -/// module!{ -/// type: MyKernelModule, -/// name: b"my_kernel_module", -/// author: b"Rust for Linux Contributors", -/// description: b"My very own kernel module!", -/// license: b"GPL v2", -/// params: { -/// my_i32: i32 { -/// default: 42, -/// permissions: 0o000, -/// description: b"Example of i32", -/// }, -/// writeable_i32: i32 { -/// default: 42, -/// permissions: 0o644, -/// description: b"Example of i32", -/// }, -/// }, -/// } -/// -/// struct MyKernelModule; -/// -/// impl KernelModule for MyKernelModule { -/// fn init() -> Result { -/// // If the parameter is writeable, then the kparam lock must be -/// // taken to read the parameter: -/// { -/// let lock = THIS_MODULE.kernel_param_lock(); -/// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock)); -/// } -/// // If the parameter is read only, it can be read without locking -/// // the kernel parameters: -/// pr_info!("i32 param is: {}\n", my_i32.read()); -/// Ok(MyKernelModule) -/// } -/// } -/// ``` -/// -/// # Supported argument types -/// - `type`: type which implements the [`KernelModule`] trait (required). -/// - `name`: byte array of the name of the kernel module (required). -/// - `author`: byte array of the author of the kernel module. -/// - `description`: byte array of the description of the kernel module. -/// - `license`: byte array of the license of the kernel module (required). -/// - `alias`: byte array of alias name of the kernel module. -/// - `alias_rtnl_link`: byte array of the `rtnl_link_alias` of the kernel module (mutually exclusive with `alias`). -/// - `params`: parameters for the kernel module, as described below. -/// -/// # Supported parameter types -/// -/// - `bool`: Corresponds to C `bool` param type. -/// - `i8`: No equivalent C param type. -/// - `u8`: Corresponds to C `char` param type. -/// - `i16`: Corresponds to C `short` param type. -/// - `u16`: Corresponds to C `ushort` param type. -/// - `i32`: Corresponds to C `int` param type. -/// - `u32`: Corresponds to C `uint` param type. -/// - `i64`: No equivalent C param type. -/// - `u64`: Corresponds to C `ullong` param type. -/// - `isize`: No equivalent C param type. -/// - `usize`: No equivalent C param type. -/// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice. -/// - `ArrayParam`: Corresponds to C parameters created using `module_param_array`. An array -/// of `T`'s of length at **most** `N`. -/// -/// `invbool` is unsupported: it was only ever used in a few modules. -/// Consider using a `bool` and inverting the logic instead. -#[proc_macro] pub fn module(ts: TokenStream) -> TokenStream { let mut it = ts.into_iter(); @@ -775,34 +691,6 @@ pub fn module(ts: TokenStream) -> TokenStream { ).parse().expect("Error parsing formatted string into token stream.") } -/// Declares a kernel module that exposes a single misc device. -/// -/// The `type` argument should be a type which implements the [`FileOpener`] trait. Also accepts -/// various forms of kernel metadata. -/// -/// [`FileOpener`]: ../kernel/file_operations/trait.FileOpener.html -/// -/// # Examples -/// -/// ```rust,no_run -/// use kernel::prelude::*; -/// -/// module_misc_device! { -/// type: MyFile, -/// name: b"my_miscdev_kernel_module", -/// author: b"Rust for Linux Contributors", -/// description: b"My very own misc device kernel module!", -/// license: b"GPL v2", -/// } -/// -/// #[derive(Default)] -/// struct MyFile; -/// -/// impl kernel::file_operations::FileOperations for MyFile { -/// kernel::declare_file_operations!(); -/// } -/// ``` -#[proc_macro] pub fn module_misc_device(ts: TokenStream) -> TokenStream { let mut it = ts.into_iter(); @@ -821,7 +709,7 @@ pub fn module_misc_device(ts: TokenStream) -> TokenStream { fn init() -> kernel::Result {{ Ok(Self {{ _dev: kernel::miscdev::Registration::new_pinned::<{type_}>( - kernel::c_str!(\"{name}\"), + kernel::c_str!({name}), None, (), )?, diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index e88d4fc9eb3000..2a7b22be642bbe 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -63,13 +63,13 @@ def append_crate(display_name, root_module, is_workspace_member, deps, cfg): ) append_crate( - "module", - srctree / "rust" / "module.rs", + "macros", + srctree / "rust" / "macros" / "lib.rs", True, [], [], ) - crates[-1]["proc_macro_dylib_path"] = "rust/libmodule.so" + crates[-1]["proc_macro_dylib_path"] = "rust/libmacros.so" append_crate( "build_error", @@ -83,7 +83,7 @@ def append_crate(display_name, root_module, is_workspace_member, deps, cfg): "kernel", srctree / "rust" / "kernel" / "lib.rs", True, - ["core", "alloc", "module", "build_error"], + ["core", "alloc", "macros", "build_error"], cfg, ) crates[-1]["env"]["RUST_BINDINGS_FILE"] = str(bindings_file.resolve(True)) From fd4ef65f82cccfa600887dedb278562ea741d30b Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 10 May 2021 21:31:11 +0100 Subject: [PATCH 2/9] rust: Vendor literal parsing from `syn` crate `module!` macro currently have some ad-hoc literal parsing. `syn` crate provides some proper parsing for us to use. Signed-off-by: Gary Guo --- rust/macros/lib.rs | 1 + rust/macros/syn/lit.rs | 883 +++++++++++++++++++++++++++++++++++++++++ rust/macros/syn/mod.rs | 11 + 3 files changed, 895 insertions(+) create mode 100644 rust/macros/syn/lit.rs create mode 100644 rust/macros/syn/mod.rs diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index a0c882dc8aa81d..47768807b3b209 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -2,6 +2,7 @@ //! Crate for all kernel procedural macros. mod module; +mod syn; use proc_macro::TokenStream; diff --git a/rust/macros/syn/lit.rs b/rust/macros/syn/lit.rs new file mode 100644 index 00000000000000..0daf3097dc357a --- /dev/null +++ b/rust/macros/syn/lit.rs @@ -0,0 +1,883 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 + +// Adapted from https://github.com/dtolnay/syn/blob/1.0.72/src/lit.rs +// Changes made compared to upstream: +// * Removed code that depends on types and macros defined elsewhere in syn. +// * Removed dependency on `BigInt` and `unicode-xid`. +// * Removed `From` and `new` from `LitInt` and `LitFloat` which are +// unlikely to be used by us. + +use proc_macro::{Literal, Span}; + +/// A Rust literal such as a string or integer or boolean. +/// +/// # Syntax tree enum +/// +/// This type is a [syntax tree enum]. +/// +/// [syntax tree enum]: crate::Expr#syntax-tree-enums +pub enum Lit { + /// A UTF-8 string literal: `"foo"`. + Str(LitStr), + + /// A byte string literal: `b"foo"`. + ByteStr(LitByteStr), + + /// A byte literal: `b'f'`. + Byte(LitByte), + + /// A character literal: `'a'`. + Char(LitChar), + + /// An integer literal: `1` or `1u16`. + Int(LitInt), + + /// A floating point literal: `1f64` or `1.0e10f64`. + /// + /// Must be finite. May not be infinite or NaN. + Float(LitFloat), + + /// A boolean literal: `true` or `false`. + Bool(LitBool), + + /// A raw token literal not interpreted by Syn. + Verbatim(Literal), +} + +/// A UTF-8 string literal: `"foo"`. +pub struct LitStr { + repr: Box, +} + +/// A byte string literal: `b"foo"`. +pub struct LitByteStr { + repr: Box, +} + +/// A byte literal: `b'f'`. +pub struct LitByte { + repr: Box, +} + +/// A character literal: `'a'`. +pub struct LitChar { + repr: Box, +} + +struct LitRepr { + token: Literal, + suffix: Box, +} + +/// An integer literal: `1` or `1u16`. +pub struct LitInt { + repr: Box, +} + +struct LitIntRepr { + token: Literal, + digits: Box, + suffix: Box, +} + +/// A floating point literal: `1f64` or `1.0e10f64`. +/// +/// Must be finite. May not be infinite or NaN. +pub struct LitFloat { + repr: Box, +} + +struct LitFloatRepr { + token: Literal, + digits: Box, + suffix: Box, +} + +/// A boolean literal: `true` or `false`. +pub struct LitBool { + pub value: bool, + pub span: Span, +} + +impl LitStr { + pub fn new(value: &str, span: Span) -> Self { + let mut token = Literal::string(value); + token.set_span(span); + LitStr { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> String { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_str(&repr); + String::from(value) + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitByteStr { + pub fn new(value: &[u8], span: Span) -> Self { + let mut token = Literal::byte_string(value); + token.set_span(span); + LitByteStr { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> Vec { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_byte_str(&repr); + value + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitByte { + pub fn new(value: u8, span: Span) -> Self { + let mut token = Literal::u8_suffixed(value); + token.set_span(span); + LitByte { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> u8 { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_byte(&repr); + value + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitChar { + pub fn new(value: char, span: Span) -> Self { + let mut token = Literal::character(value); + token.set_span(span); + LitChar { + repr: Box::new(LitRepr { + token, + suffix: Box::::default(), + }), + } + } + + pub fn value(&self) -> char { + let repr = self.repr.token.to_string(); + let (value, _suffix) = value::parse_lit_char(&repr); + value + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } +} + +impl LitInt { + pub fn base10_digits(&self) -> &str { + &self.repr.digits + } + + pub fn suffix(&self) -> &str { + &self.repr.suffix + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } +} + +impl LitFloat { + pub fn suffix(&self) -> &str { + &self.repr.suffix + } + + pub fn span(&self) -> Span { + self.repr.token.span() + } + + pub fn set_span(&mut self, span: Span) { + self.repr.token.set_span(span) + } +} + +impl LitBool { + pub fn new(value: bool, span: Span) -> Self { + LitBool { value, span } + } + + pub fn value(&self) -> bool { + self.value + } + + pub fn span(&self) -> Span { + self.span + } + + pub fn set_span(&mut self, span: Span) { + self.span = span; + } +} + +mod value { + use super::*; + use std::char; + use std::ops::{Index, RangeFrom}; + + impl Lit { + /// Interpret a Syn literal from a proc-macro2 literal. + pub fn new(token: Literal) -> Self { + let repr = token.to_string(); + + match byte(&repr, 0) { + b'"' | b'r' => { + let (_, suffix) = parse_lit_str(&repr); + return Lit::Str(LitStr { + repr: Box::new(LitRepr { token, suffix }), + }); + } + b'b' => match byte(&repr, 1) { + b'"' | b'r' => { + let (_, suffix) = parse_lit_byte_str(&repr); + return Lit::ByteStr(LitByteStr { + repr: Box::new(LitRepr { token, suffix }), + }); + } + b'\'' => { + let (_, suffix) = parse_lit_byte(&repr); + return Lit::Byte(LitByte { + repr: Box::new(LitRepr { token, suffix }), + }); + } + _ => {} + }, + b'\'' => { + let (_, suffix) = parse_lit_char(&repr); + return Lit::Char(LitChar { + repr: Box::new(LitRepr { token, suffix }), + }); + } + b'0'..=b'9' | b'-' => { + if let Some((digits, suffix)) = parse_lit_int(&repr) { + return Lit::Int(LitInt { + repr: Box::new(LitIntRepr { + token, + digits, + suffix, + }), + }); + } + if let Some((digits, suffix)) = parse_lit_float(&repr) { + return Lit::Float(LitFloat { + repr: Box::new(LitFloatRepr { + token, + digits, + suffix, + }), + }); + } + } + b't' | b'f' => { + if repr == "true" || repr == "false" { + return Lit::Bool(LitBool { + value: repr == "true", + span: token.span(), + }); + } + } + _ => {} + } + + panic!("Unrecognized literal: `{}`", repr); + } + + pub fn suffix(&self) -> &str { + match self { + Lit::Str(lit) => lit.suffix(), + Lit::ByteStr(lit) => lit.suffix(), + Lit::Byte(lit) => lit.suffix(), + Lit::Char(lit) => lit.suffix(), + Lit::Int(lit) => lit.suffix(), + Lit::Float(lit) => lit.suffix(), + Lit::Bool(_) | Lit::Verbatim(_) => "", + } + } + + pub fn span(&self) -> Span { + match self { + Lit::Str(lit) => lit.span(), + Lit::ByteStr(lit) => lit.span(), + Lit::Byte(lit) => lit.span(), + Lit::Char(lit) => lit.span(), + Lit::Int(lit) => lit.span(), + Lit::Float(lit) => lit.span(), + Lit::Bool(lit) => lit.span, + Lit::Verbatim(lit) => lit.span(), + } + } + + pub fn set_span(&mut self, span: Span) { + match self { + Lit::Str(lit) => lit.set_span(span), + Lit::ByteStr(lit) => lit.set_span(span), + Lit::Byte(lit) => lit.set_span(span), + Lit::Char(lit) => lit.set_span(span), + Lit::Int(lit) => lit.set_span(span), + Lit::Float(lit) => lit.set_span(span), + Lit::Bool(lit) => lit.span = span, + Lit::Verbatim(lit) => lit.set_span(span), + } + } + } + + /// Get the byte at offset idx, or a default of `b'\0'` if we're looking + /// past the end of the input buffer. + pub fn byte + ?Sized>(s: &S, idx: usize) -> u8 { + let s = s.as_ref(); + if idx < s.len() { + s[idx] + } else { + 0 + } + } + + fn next_chr(s: &str) -> char { + s.chars().next().unwrap_or('\0') + } + + // Returns (content, suffix). + pub fn parse_lit_str(s: &str) -> (Box, Box) { + match byte(s, 0) { + b'"' => parse_lit_str_cooked(s), + b'r' => parse_lit_str_raw(s), + _ => unreachable!(), + } + } + + // Clippy false positive + // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 + #[allow(clippy::needless_continue)] + fn parse_lit_str_cooked(mut s: &str) -> (Box, Box) { + assert_eq!(byte(s, 0), b'"'); + s = &s[1..]; + + let mut content = String::new(); + 'outer: loop { + let ch = match byte(s, 0) { + b'"' => break, + b'\\' => { + let b = byte(s, 1); + s = &s[2..]; + match b { + b'x' => { + let (byte, rest) = backslash_x(s); + s = rest; + assert!(byte <= 0x80, "Invalid \\x byte in string literal"); + char::from_u32(u32::from(byte)).unwrap() + } + b'u' => { + let (chr, rest) = backslash_u(s); + s = rest; + chr + } + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + b'\\' => '\\', + b'0' => '\0', + b'\'' => '\'', + b'"' => '"', + b'\r' | b'\n' => loop { + let ch = next_chr(s); + if ch.is_whitespace() { + s = &s[ch.len_utf8()..]; + } else { + continue 'outer; + } + }, + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b'\r' => { + assert_eq!(byte(s, 1), b'\n', "Bare CR not allowed in string"); + s = &s[2..]; + '\n' + } + _ => { + let ch = next_chr(s); + s = &s[ch.len_utf8()..]; + ch + } + }; + content.push(ch); + } + + assert!(s.starts_with('"')); + let content = content.into_boxed_str(); + let suffix = s[1..].to_owned().into_boxed_str(); + (content, suffix) + } + + fn parse_lit_str_raw(mut s: &str) -> (Box, Box) { + assert_eq!(byte(s, 0), b'r'); + s = &s[1..]; + + let mut pounds = 0; + while byte(s, pounds) == b'#' { + pounds += 1; + } + assert_eq!(byte(s, pounds), b'"'); + let close = s.rfind('"').unwrap(); + for end in s[close + 1..close + 1 + pounds].bytes() { + assert_eq!(end, b'#'); + } + + let content = s[pounds + 1..close].to_owned().into_boxed_str(); + let suffix = s[close + 1 + pounds..].to_owned().into_boxed_str(); + (content, suffix) + } + + // Returns (content, suffix). + pub fn parse_lit_byte_str(s: &str) -> (Vec, Box) { + assert_eq!(byte(s, 0), b'b'); + match byte(s, 1) { + b'"' => parse_lit_byte_str_cooked(s), + b'r' => parse_lit_byte_str_raw(s), + _ => unreachable!(), + } + } + + // Clippy false positive + // https://github.com/rust-lang-nursery/rust-clippy/issues/2329 + #[allow(clippy::needless_continue)] + fn parse_lit_byte_str_cooked(mut s: &str) -> (Vec, Box) { + assert_eq!(byte(s, 0), b'b'); + assert_eq!(byte(s, 1), b'"'); + s = &s[2..]; + + // We're going to want to have slices which don't respect codepoint boundaries. + let mut v = s.as_bytes(); + + let mut out = Vec::new(); + 'outer: loop { + let byte = match byte(v, 0) { + b'"' => break, + b'\\' => { + let b = byte(v, 1); + v = &v[2..]; + match b { + b'x' => { + let (b, rest) = backslash_x(v); + v = rest; + b + } + b'n' => b'\n', + b'r' => b'\r', + b't' => b'\t', + b'\\' => b'\\', + b'0' => b'\0', + b'\'' => b'\'', + b'"' => b'"', + b'\r' | b'\n' => loop { + let byte = byte(v, 0); + let ch = char::from_u32(u32::from(byte)).unwrap(); + if ch.is_whitespace() { + v = &v[1..]; + } else { + continue 'outer; + } + }, + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b'\r' => { + assert_eq!(byte(v, 1), b'\n', "Bare CR not allowed in string"); + v = &v[2..]; + b'\n' + } + b => { + v = &v[1..]; + b + } + }; + out.push(byte); + } + + assert_eq!(byte(v, 0), b'"'); + let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); + (out, suffix) + } + + fn parse_lit_byte_str_raw(s: &str) -> (Vec, Box) { + assert_eq!(byte(s, 0), b'b'); + let (value, suffix) = parse_lit_str_raw(&s[1..]); + (String::from(value).into_bytes(), suffix) + } + + // Returns (value, suffix). + pub fn parse_lit_byte(s: &str) -> (u8, Box) { + assert_eq!(byte(s, 0), b'b'); + assert_eq!(byte(s, 1), b'\''); + + // We're going to want to have slices which don't respect codepoint boundaries. + let mut v = s[2..].as_bytes(); + + let b = match byte(v, 0) { + b'\\' => { + let b = byte(v, 1); + v = &v[2..]; + match b { + b'x' => { + let (b, rest) = backslash_x(v); + v = rest; + b + } + b'n' => b'\n', + b'r' => b'\r', + b't' => b'\t', + b'\\' => b'\\', + b'0' => b'\0', + b'\'' => b'\'', + b'"' => b'"', + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + b => { + v = &v[1..]; + b + } + }; + + assert_eq!(byte(v, 0), b'\''); + let suffix = s[s.len() - v.len() + 1..].to_owned().into_boxed_str(); + (b, suffix) + } + + // Returns (value, suffix). + pub fn parse_lit_char(mut s: &str) -> (char, Box) { + assert_eq!(byte(s, 0), b'\''); + s = &s[1..]; + + let ch = match byte(s, 0) { + b'\\' => { + let b = byte(s, 1); + s = &s[2..]; + match b { + b'x' => { + let (byte, rest) = backslash_x(s); + s = rest; + assert!(byte <= 0x80, "Invalid \\x byte in string literal"); + char::from_u32(u32::from(byte)).unwrap() + } + b'u' => { + let (chr, rest) = backslash_u(s); + s = rest; + chr + } + b'n' => '\n', + b'r' => '\r', + b't' => '\t', + b'\\' => '\\', + b'0' => '\0', + b'\'' => '\'', + b'"' => '"', + b => panic!("unexpected byte {:?} after \\ character in byte literal", b), + } + } + _ => { + let ch = next_chr(s); + s = &s[ch.len_utf8()..]; + ch + } + }; + assert_eq!(byte(s, 0), b'\''); + let suffix = s[1..].to_owned().into_boxed_str(); + (ch, suffix) + } + + fn backslash_x(s: &S) -> (u8, &S) + where + S: Index, Output = S> + AsRef<[u8]> + ?Sized, + { + let mut ch = 0; + let b0 = byte(s, 0); + let b1 = byte(s, 1); + ch += 0x10 + * match b0 { + b'0'..=b'9' => b0 - b'0', + b'a'..=b'f' => 10 + (b0 - b'a'), + b'A'..=b'F' => 10 + (b0 - b'A'), + _ => panic!("unexpected non-hex character after \\x"), + }; + ch += match b1 { + b'0'..=b'9' => b1 - b'0', + b'a'..=b'f' => 10 + (b1 - b'a'), + b'A'..=b'F' => 10 + (b1 - b'A'), + _ => panic!("unexpected non-hex character after \\x"), + }; + (ch, &s[2..]) + } + + fn backslash_u(mut s: &str) -> (char, &str) { + if byte(s, 0) != b'{' { + panic!("{}", "expected { after \\u"); + } + s = &s[1..]; + + let mut ch = 0; + let mut digits = 0; + loop { + let b = byte(s, 0); + let digit = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => 10 + b - b'a', + b'A'..=b'F' => 10 + b - b'A', + b'_' if digits > 0 => { + s = &s[1..]; + continue; + } + b'}' if digits == 0 => panic!("invalid empty unicode escape"), + b'}' => break, + _ => panic!("unexpected non-hex character after \\u"), + }; + if digits == 6 { + panic!("overlong unicode escape (must have at most 6 hex digits)"); + } + ch *= 0x10; + ch += u32::from(digit); + digits += 1; + s = &s[1..]; + } + assert!(byte(s, 0) == b'}'); + s = &s[1..]; + + if let Some(ch) = char::from_u32(ch) { + (ch, s) + } else { + panic!("character code {:x} is not a valid unicode character", ch); + } + } + + // Returns base 10 digits and suffix. + pub fn parse_lit_int(mut s: &str) -> Option<(Box, Box)> { + let negative = byte(s, 0) == b'-'; + if negative { + s = &s[1..]; + } + + let base = match (byte(s, 0), byte(s, 1)) { + (b'0', b'x') => { + s = &s[2..]; + 16 + } + (b'0', b'o') => { + s = &s[2..]; + 8 + } + (b'0', b'b') => { + s = &s[2..]; + 2 + } + (b'0'..=b'9', _) => 10, + _ => return None, + }; + + let mut value = 0u128; + 'outer: loop { + let b = byte(s, 0); + let digit = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' if base > 10 => b - b'a' + 10, + b'A'..=b'F' if base > 10 => b - b'A' + 10, + b'_' => { + s = &s[1..]; + continue; + } + // If looking at a floating point literal, we don't want to + // consider it an integer. + b'.' if base == 10 => return None, + b'e' | b'E' if base == 10 => { + let mut has_exp = false; + for (i, b) in s[1..].bytes().enumerate() { + match b { + b'_' => {} + b'-' | b'+' => return None, + b'0'..=b'9' => has_exp = true, + _ => { + let _suffix = &s[1 + i..]; + if has_exp { + return None; + } else { + break 'outer; + } + } + } + } + if has_exp { + return None; + } else { + break; + } + } + _ => break, + }; + + if digit >= base { + return None; + } + + value *= base as u128; + value += digit as u128; + s = &s[1..]; + } + + let suffix = s; + let mut repr = value.to_string(); + if negative { + repr.insert(0, '-'); + } + Some((repr.into_boxed_str(), suffix.to_owned().into_boxed_str())) + } + + // Returns base 10 digits and suffix. + pub fn parse_lit_float(input: &str) -> Option<(Box, Box)> { + // Rust's floating point literals are very similar to the ones parsed by + // the standard library, except that rust's literals can contain + // ignorable underscores. Let's remove those underscores. + + let mut bytes = input.to_owned().into_bytes(); + + let start = (*bytes.get(0)? == b'-') as usize; + match bytes.get(start)? { + b'0'..=b'9' => {} + _ => return None, + } + + let mut read = start; + let mut write = start; + let mut has_dot = false; + let mut has_e = false; + let mut has_sign = false; + let mut has_exponent = false; + while read < bytes.len() { + match bytes[read] { + b'_' => { + // Don't increase write + read += 1; + continue; + } + b'0'..=b'9' => { + if has_e { + has_exponent = true; + } + bytes[write] = bytes[read]; + } + b'.' => { + if has_e || has_dot { + return None; + } + has_dot = true; + bytes[write] = b'.'; + } + b'e' | b'E' => { + match bytes[read + 1..] + .iter() + .find(|b| **b != b'_') + .unwrap_or(&b'\0') + { + b'-' | b'+' | b'0'..=b'9' => {} + _ => break, + } + if has_e { + if has_exponent { + break; + } else { + return None; + } + } + has_e = true; + bytes[write] = b'e'; + } + b'-' | b'+' => { + if has_sign || has_exponent || !has_e { + return None; + } + has_sign = true; + if bytes[read] == b'-' { + bytes[write] = bytes[read]; + } else { + // Omit '+' + read += 1; + continue; + } + } + _ => break, + } + read += 1; + write += 1; + } + + if has_e && !has_exponent { + return None; + } + + let mut digits = String::from_utf8(bytes).unwrap(); + let suffix = digits.split_off(read); + digits.truncate(write); + Some((digits.into_boxed_str(), suffix.into_boxed_str())) + } +} diff --git a/rust/macros/syn/mod.rs b/rust/macros/syn/mod.rs new file mode 100644 index 00000000000000..7f053eed8b1b6b --- /dev/null +++ b/rust/macros/syn/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 + +//! Shared parsing functions for use in procedural macros. +//! +//! These code are from [syn 1.0.72](https://github.com/dtolnay/syn). + +#![allow(dead_code)] + +mod lit; + +pub use lit::{Lit, LitByteStr, LitStr}; From ab4c335aecc93c9d0334fc9dc50f2a1ab2413adf Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 17 May 2021 20:23:04 +0100 Subject: [PATCH 3/9] rust: accept `str` instead of `bstr` in `module!` Signed-off-by: Gary Guo --- drivers/android/rust_binder.rs | 8 +-- drivers/char/hw_random/bcm2835_rng_rust.rs | 8 +-- rust/macros/lib.rs | 20 +++--- rust/macros/module.rs | 83 +++++++++++----------- samples/rust/rust_chrdev.rs | 8 +-- samples/rust/rust_minimal.rs | 8 +-- samples/rust/rust_miscdev.rs | 8 +-- samples/rust/rust_module_parameters.rs | 20 +++--- samples/rust/rust_print.rs | 8 +-- samples/rust/rust_random.rs | 8 +-- samples/rust/rust_semaphore.rs | 8 +-- samples/rust/rust_stack_probing.rs | 8 +-- samples/rust/rust_sync.rs | 8 +-- 13 files changed, 103 insertions(+), 100 deletions(-) diff --git a/drivers/android/rust_binder.rs b/drivers/android/rust_binder.rs index 617f7a8cab832b..939b6f223bf1b6 100644 --- a/drivers/android/rust_binder.rs +++ b/drivers/android/rust_binder.rs @@ -31,10 +31,10 @@ use {context::Context, thread::Thread}; module! { type: BinderModule, - name: b"rust_binder", - author: b"Wedson Almeida Filho", - description: b"Android Binder", - license: b"GPL v2", + name: "rust_binder", + author: "Wedson Almeida Filho", + description: "Android Binder", + license: "GPL v2", } enum Either { diff --git a/drivers/char/hw_random/bcm2835_rng_rust.rs b/drivers/char/hw_random/bcm2835_rng_rust.rs index 9ae5395172c317..3dc452df9e5655 100644 --- a/drivers/char/hw_random/bcm2835_rng_rust.rs +++ b/drivers/char/hw_random/bcm2835_rng_rust.rs @@ -13,10 +13,10 @@ use kernel::{c_str, platdev}; module! { type: RngModule, - name: b"bcm2835_rng_rust", - author: b"Rust for Linux Contributors", - description: b"BCM2835 Random Number Generator (RNG) driver", - license: b"GPL v2", + name: "bcm2835_rng_rust", + author: "Rust for Linux Contributors", + description: "BCM2835 Random Number Generator (RNG) driver", + license: "GPL v2", } struct RngModule { diff --git a/rust/macros/lib.rs b/rust/macros/lib.rs index 47768807b3b209..03e39009e8d0d6 100644 --- a/rust/macros/lib.rs +++ b/rust/macros/lib.rs @@ -22,20 +22,20 @@ use proc_macro::TokenStream; /// /// module!{ /// type: MyKernelModule, -/// name: b"my_kernel_module", -/// author: b"Rust for Linux Contributors", -/// description: b"My very own kernel module!", -/// license: b"GPL v2", +/// name: "my_kernel_module", +/// author: "Rust for Linux Contributors", +/// description: "My very own kernel module!", +/// license: "GPL v2", /// params: { /// my_i32: i32 { /// default: 42, /// permissions: 0o000, -/// description: b"Example of i32", +/// description: "Example of i32", /// }, /// writeable_i32: i32 { /// default: 42, /// permissions: 0o644, -/// description: b"Example of i32", +/// description: "Example of i32", /// }, /// }, /// } @@ -108,10 +108,10 @@ pub fn module(ts: TokenStream) -> TokenStream { /// /// module_misc_device! { /// type: MyFile, -/// name: b"my_miscdev_kernel_module", -/// author: b"Rust for Linux Contributors", -/// description: b"My very own misc device kernel module!", -/// license: b"GPL v2", +/// name: "my_miscdev_kernel_module", +/// author: "Rust for Linux Contributors", +/// description: "My very own misc device kernel module!", +/// license: "GPL v2", /// } /// /// #[derive(Default)] diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 0a155b8d4c9f7f..247ef5e8d1e8eb 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -5,7 +5,8 @@ #![deny(clippy::perf)] #![deny(clippy::style)] -use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree}; +use crate::syn::Lit; +use proc_macro::{token_stream, Delimiter, Group, Literal, TokenStream, TokenTree}; fn try_ident(it: &mut token_stream::IntoIter) -> Option { if let Some(TokenTree::Ident(ident)) = it.next() { @@ -15,21 +16,21 @@ fn try_ident(it: &mut token_stream::IntoIter) -> Option { } } -fn try_literal(it: &mut token_stream::IntoIter) -> Option { +fn try_literal(it: &mut token_stream::IntoIter) -> Option { if let Some(TokenTree::Literal(literal)) = it.next() { - Some(literal.to_string()) + Some(literal) } else { None } } -fn try_byte_string(it: &mut token_stream::IntoIter) -> Option { - try_literal(it).and_then(|byte_string| { - if byte_string.starts_with("b\"") && byte_string.ends_with('\"') { - Some(byte_string[2..byte_string.len() - 1].to_string()) - } else { - None +fn try_string(it: &mut token_stream::IntoIter) -> Option { + try_literal(it).and_then(|literal| match Lit::new(literal) { + Lit::Str(s) => { + assert!(s.suffix().is_empty(), "Unexpected suffix"); + Some(s.value()) } + _ => None, }) } @@ -45,7 +46,7 @@ fn expect_punct(it: &mut token_stream::IntoIter) -> char { } } -fn expect_literal(it: &mut token_stream::IntoIter) -> String { +fn expect_literal(it: &mut token_stream::IntoIter) -> Literal { try_literal(it).expect("Expected Literal") } @@ -57,8 +58,8 @@ fn expect_group(it: &mut token_stream::IntoIter) -> Group { } } -fn expect_byte_string(it: &mut token_stream::IntoIter) -> String { - try_byte_string(it).expect("Expected byte string") +fn expect_string(it: &mut token_stream::IntoIter) -> String { + try_string(it).expect("Expected string") } #[derive(Clone, PartialEq)] @@ -71,7 +72,7 @@ fn expect_array_fields(it: &mut token_stream::IntoIter) -> ParamType { assert_eq!(expect_punct(it), '<'); let vals = expect_ident(it); assert_eq!(expect_punct(it), ','); - let max_length_str = expect_literal(it); + let max_length_str = expect_literal(it).to_string(); let max_length = max_length_str .parse::() .expect("Expected usize length"); @@ -102,15 +103,15 @@ fn expect_end(it: &mut token_stream::IntoIter) { fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); - let literal = expect_literal(it); + let literal = expect_literal(it).to_string(); assert_eq!(expect_punct(it), ','); literal } -fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { +fn get_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); - let byte_string = expect_byte_string(it); + let byte_string = expect_string(it); assert_eq!(expect_punct(it), ','); byte_string } @@ -125,14 +126,14 @@ fn __build_modinfo_string_base( let string = if builtin { // Built-in modules prefix their modinfo strings by `module.`. format!( - "{module}.{field}={content}", + "{module}.{field}={content}\0", module = module, field = field, content = content ) } else { // Loadable modules' modinfo strings go as-is. - format!("{field}={content}", field = field, content = content) + format!("{field}={content}\0", field = field, content = content) }; format!( @@ -140,7 +141,7 @@ fn __build_modinfo_string_base( {cfg} #[link_section = \".modinfo\"] #[used] - pub static {variable}: [u8; {length}] = *b\"{string}\\0\"; + pub static {variable}: [u8; {length}] = *{string}; ", cfg = if builtin { "#[cfg(not(MODULE))]" @@ -148,8 +149,8 @@ fn __build_modinfo_string_base( "#[cfg(MODULE)]" }, variable = variable, - length = string.len() + 1, - string = string, + length = string.len(), + string = Literal::byte_string(string.as_bytes()), ) } @@ -242,10 +243,14 @@ fn try_simple_param_val( match param_type { "bool" => Box::new(|param_it| try_ident(param_it)), "str" => Box::new(|param_it| { - try_byte_string(param_it) - .map(|s| format!("kernel::module_param::StringParam::Ref(b\"{}\")", s)) + try_string(param_it).map(|s| { + format!( + "kernel::module_param::StringParam::Ref({})", + Literal::byte_string(s.as_bytes()) + ) + }) }), - _ => Box::new(|param_it| try_literal(param_it)), + _ => Box::new(|param_it| try_literal(param_it).map(|x| x.to_string())), } } @@ -349,14 +354,12 @@ impl ModuleInfo { match key.as_str() { "type" => info.type_ = expect_ident(it), - "name" => info.name = expect_byte_string(it), - "author" => info.author = Some(expect_byte_string(it)), - "description" => info.description = Some(expect_byte_string(it)), - "license" => info.license = expect_byte_string(it), - "alias" => info.alias = Some(expect_byte_string(it)), - "alias_rtnl_link" => { - info.alias = Some(format!("rtnl-link-{}", expect_byte_string(it))) - } + "name" => info.name = expect_string(it), + "author" => info.author = Some(expect_string(it)), + "description" => info.description = Some(expect_string(it)), + "license" => info.license = expect_string(it), + "alias" => info.alias = Some(expect_string(it)), + "alias_rtnl_link" => info.alias = Some(format!("rtnl-link-{}", expect_string(it))), "params" => info.params = Some(expect_group(it)), _ => panic!( "Unknown key \"{}\". Valid keys are: {:?}.", @@ -426,7 +429,7 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut param_it = group.stream().into_iter(); let param_default = get_default(¶m_type, &mut param_it); let param_permissions = get_literal(&mut param_it, "permissions"); - let param_description = get_byte_string(&mut param_it, "description"); + let param_description = get_string(&mut param_it, "description"); expect_end(&mut param_it); // TODO: more primitive types @@ -719,29 +722,29 @@ pub fn module_misc_device(ts: TokenStream) -> TokenStream { kernel::prelude::module! {{ type: {module}, - name: b\"{name}\", + name: {name}, {author} {description} - license: b\"{license}\", + license: {license}, {alias} }} ", module = module, type_ = info.type_, - name = info.name, + name = Literal::string(&info.name), author = info .author - .map(|v| format!("author: b\"{}\",", v)) + .map(|v| format!("author: {},", Literal::string(&v))) .unwrap_or_else(|| "".to_string()), description = info .description - .map(|v| format!("description: b\"{}\",", v)) + .map(|v| format!("description: {},", Literal::string(&v))) .unwrap_or_else(|| "".to_string()), alias = info .alias - .map(|v| format!("alias: b\"{}\",", v)) + .map(|v| format!("alias: {},", Literal::string(&v))) .unwrap_or_else(|| "".to_string()), - license = info.license + license = Literal::string(&info.license) ) .parse() .expect("Error parsing formatted string into token stream.") diff --git a/samples/rust/rust_chrdev.rs b/samples/rust/rust_chrdev.rs index 0e8151dd783940..f32fcf2c44b4bd 100644 --- a/samples/rust/rust_chrdev.rs +++ b/samples/rust/rust_chrdev.rs @@ -12,10 +12,10 @@ use kernel::{c_str, chrdev, file_operations::FileOperations}; module! { type: RustChrdev, - name: b"rust_chrdev", - author: b"Rust for Linux Contributors", - description: b"Rust character device sample", - license: b"GPL v2", + name: "rust_chrdev", + author: "Rust for Linux Contributors", + description: "Rust character device sample", + license: "GPL v2", } #[derive(Default)] diff --git a/samples/rust/rust_minimal.rs b/samples/rust/rust_minimal.rs index 84a227021aafdd..539738c6414991 100644 --- a/samples/rust/rust_minimal.rs +++ b/samples/rust/rust_minimal.rs @@ -9,10 +9,10 @@ use kernel::prelude::*; module! { type: RustMinimal, - name: b"rust_minimal", - author: b"Rust for Linux Contributors", - description: b"Rust minimal sample", - license: b"GPL v2", + name: "rust_minimal", + author: "Rust for Linux Contributors", + description: "Rust minimal sample", + license: "GPL v2", } struct RustMinimal { diff --git a/samples/rust/rust_miscdev.rs b/samples/rust/rust_miscdev.rs index f3293b4800904b..5b04cbab4809fb 100644 --- a/samples/rust/rust_miscdev.rs +++ b/samples/rust/rust_miscdev.rs @@ -20,10 +20,10 @@ use kernel::{ module! { type: RustMiscdev, - name: b"rust_miscdev", - author: b"Rust for Linux Contributors", - description: b"Rust miscellaneous device sample", - license: b"GPL v2", + name: "rust_miscdev", + author: "Rust for Linux Contributors", + description: "Rust miscellaneous device sample", + license: "GPL v2", } const MAX_TOKENS: usize = 3; diff --git a/samples/rust/rust_module_parameters.rs b/samples/rust/rust_module_parameters.rs index 57e59e80502759..db735497fd2561 100644 --- a/samples/rust/rust_module_parameters.rs +++ b/samples/rust/rust_module_parameters.rs @@ -9,35 +9,35 @@ use kernel::prelude::*; module! { type: RustModuleParameters, - name: b"rust_module_parameters", - author: b"Rust for Linux Contributors", - description: b"Rust module parameters sample", - license: b"GPL v2", + name: "rust_module_parameters", + author: "Rust for Linux Contributors", + description: "Rust module parameters sample", + license: "GPL v2", params: { my_bool: bool { default: true, permissions: 0, - description: b"Example of bool", + description: "Example of bool", }, my_i32: i32 { default: 42, permissions: 0o644, - description: b"Example of i32", + description: "Example of i32", }, my_str: str { - default: b"default str val", + default: "default str val", permissions: 0o644, - description: b"Example of a string param", + description: "Example of a string param", }, my_usize: usize { default: 42, permissions: 0o644, - description: b"Example of usize", + description: "Example of usize", }, my_array: ArrayParam { default: [0, 1], permissions: 0, - description: b"Example of array", + description: "Example of array", }, }, } diff --git a/samples/rust/rust_print.rs b/samples/rust/rust_print.rs index a457e1e87fe50c..572fdd1e807e12 100644 --- a/samples/rust/rust_print.rs +++ b/samples/rust/rust_print.rs @@ -9,10 +9,10 @@ use kernel::prelude::*; module! { type: RustPrint, - name: b"rust_print", - author: b"Rust for Linux Contributors", - description: b"Rust printing macros sample", - license: b"GPL v2", + name: "rust_print", + author: "Rust for Linux Contributors", + description: "Rust printing macros sample", + license: "GPL v2", } struct RustPrint; diff --git a/samples/rust/rust_random.rs b/samples/rust/rust_random.rs index 2b42956073c6d8..2e6aa3b2f3a47e 100644 --- a/samples/rust/rust_random.rs +++ b/samples/rust/rust_random.rs @@ -54,8 +54,8 @@ impl FileOperations for RandomFile { module_misc_device! { type: RandomFile, - name: b"rust_random", - author: b"Rust for Linux Contributors", - description: b"Just use /dev/urandom: Now with early-boot safety", - license: b"GPL v2", + name: "rust_random", + author: "Rust for Linux Contributors", + description: "Just use /dev/urandom: Now with early-boot safety", + license: "GPL v2", } diff --git a/samples/rust/rust_semaphore.rs b/samples/rust/rust_semaphore.rs index b3843381f7c968..0ca6b954461a54 100644 --- a/samples/rust/rust_semaphore.rs +++ b/samples/rust/rust_semaphore.rs @@ -36,10 +36,10 @@ use kernel::{ module! { type: RustSemaphore, - name: b"rust_semaphore", - author: b"Rust for Linux Contributors", - description: b"Rust semaphore sample", - license: b"GPL v2", + name: "rust_semaphore", + author: "Rust for Linux Contributors", + description: "Rust semaphore sample", + license: "GPL v2", } struct SemaphoreInner { diff --git a/samples/rust/rust_stack_probing.rs b/samples/rust/rust_stack_probing.rs index 56f96e1e05b17e..516bc39cd9cf2f 100644 --- a/samples/rust/rust_stack_probing.rs +++ b/samples/rust/rust_stack_probing.rs @@ -10,10 +10,10 @@ use kernel::prelude::*; module! { type: RustStackProbing, - name: b"rust_stack_probing", - author: b"Rust for Linux Contributors", - description: b"Rust stack probing sample", - license: b"GPL v2", + name: "rust_stack_probing", + author: "Rust for Linux Contributors", + description: "Rust stack probing sample", + license: "GPL v2", } struct RustStackProbing; diff --git a/samples/rust/rust_sync.rs b/samples/rust/rust_sync.rs index de11da04216ca0..d31beaadb1695c 100644 --- a/samples/rust/rust_sync.rs +++ b/samples/rust/rust_sync.rs @@ -15,10 +15,10 @@ use kernel::{ module! { type: RustSync, - name: b"rust_sync", - author: b"Rust for Linux Contributors", - description: b"Rust synchronisation primitives sample", - license: b"GPL v2", + name: "rust_sync", + author: "Rust for Linux Contributors", + description: "Rust synchronisation primitives sample", + license: "GPL v2", } struct RustSync; From fc7c60f172af334c42781d71dc4785165eed2def Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 17 May 2021 21:15:34 +0100 Subject: [PATCH 4/9] rust: Vendor buffer from `syn` crate `TokenStream` itself is very hard to use for parsing, as it does not provide lookahead capability. `syn`'s buffer will provide cleanup opportunity. Signed-off-by: Gary Guo --- rust/macros/syn/buffer.rs | 318 ++++++++++++++++++++++++++++++++++++++ rust/macros/syn/mod.rs | 1 + 2 files changed, 319 insertions(+) create mode 100644 rust/macros/syn/buffer.rs diff --git a/rust/macros/syn/buffer.rs b/rust/macros/syn/buffer.rs new file mode 100644 index 00000000000000..2c86512e51f392 --- /dev/null +++ b/rust/macros/syn/buffer.rs @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 + +// Adapted from https://github.com/dtolnay/syn/blob/1.0.72/src/buffer.rs +// Changes made compared to upstream: +// * Removed code that depends on proc_macro2 +// * Removed lifetime parsing which depends on types defined elsewhere in syn. + +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Span, TokenStream, TokenTree}; +use std::marker::PhantomData; +use std::ptr; + +/// Internal type which is used instead of `TokenTree` to represent a token tree +/// within a `TokenBuffer`. +enum Entry { + // Mimicking types from proc-macro. + Group(Group, TokenBuffer), + Ident(Ident), + Punct(Punct), + Literal(Literal), + // End entries contain a raw pointer to the entry from the containing + // token tree, or null if this is the outermost level. + End(*const Entry), +} + +/// A buffer that can be efficiently traversed multiple times, unlike +/// `TokenStream` which requires a deep copy in order to traverse more than +/// once. +/// +/// *This type is available only if Syn is built with the `"parsing"` feature.* +pub struct TokenBuffer { + // NOTE: Do not derive clone on this - there are raw pointers inside which + // will be messed up. Moving the `TokenBuffer` itself is safe as the actual + // backing slices won't be moved. + data: Box<[Entry]>, +} + +impl TokenBuffer { + // NOTE: DO NOT MUTATE THE `Vec` RETURNED FROM THIS FUNCTION ONCE IT + // RETURNS, THE ADDRESS OF ITS BACKING MEMORY MUST REMAIN STABLE. + fn inner_new(stream: TokenStream, up: *const Entry) -> TokenBuffer { + // Build up the entries list, recording the locations of any Groups + // in the list to be processed later. + let mut entries = Vec::new(); + let mut seqs = Vec::new(); + for tt in stream { + match tt { + TokenTree::Ident(sym) => { + entries.push(Entry::Ident(sym)); + } + TokenTree::Punct(op) => { + entries.push(Entry::Punct(op)); + } + TokenTree::Literal(l) => { + entries.push(Entry::Literal(l)); + } + TokenTree::Group(g) => { + // Record the index of the interesting entry, and store an + // `End(null)` there temporarially. + seqs.push((entries.len(), g)); + entries.push(Entry::End(ptr::null())); + } + } + } + // Add an `End` entry to the end with a reference to the enclosing token + // stream which was passed in. + entries.push(Entry::End(up)); + + // NOTE: This is done to ensure that we don't accidentally modify the + // length of the backing buffer. The backing buffer must remain at a + // constant address after this point, as we are going to store a raw + // pointer into it. + let mut entries = entries.into_boxed_slice(); + for (idx, group) in seqs { + // We know that this index refers to one of the temporary + // `End(null)` entries, and we know that the last entry is + // `End(up)`, so the next index is also valid. + let seq_up = &entries[idx + 1] as *const Entry; + + // The end entry stored at the end of this Entry::Group should + // point to the Entry which follows the Group in the list. + let inner = Self::inner_new(group.stream(), seq_up); + entries[idx] = Entry::Group(group, inner); + } + + TokenBuffer { data: entries } + } + + /// Creates a `TokenBuffer` containing all the tokens from the input + /// `TokenStream`. + pub fn new(stream: TokenStream) -> TokenBuffer { + Self::inner_new(stream, ptr::null()) + } + + /// Creates a cursor referencing the first token in the buffer and able to + /// traverse until the end of the buffer. + pub fn begin(&self) -> Cursor { + unsafe { Cursor::create(&self.data[0], &self.data[self.data.len() - 1]) } + } +} + +/// A cheaply copyable cursor into a `TokenBuffer`. +/// +/// This cursor holds a shared reference into the immutable data which is used +/// internally to represent a `TokenStream`, and can be efficiently manipulated +/// and copied around. +/// +/// An empty `Cursor` can be created directly, or one may create a `TokenBuffer` +/// object and get a cursor to its first token with `begin()`. +/// +/// Two cursors are equal if they have the same location in the same input +/// stream, and have the same scope. +/// +/// *This type is available only if Syn is built with the `"parsing"` feature.* +pub struct Cursor<'a> { + // The current entry which the `Cursor` is pointing at. + ptr: *const Entry, + // This is the only `Entry::End(..)` object which this cursor is allowed to + // point at. All other `End` objects are skipped over in `Cursor::create`. + scope: *const Entry, + // Cursor is covariant in 'a. This field ensures that our pointers are still + // valid. + marker: PhantomData<&'a Entry>, +} + +impl<'a> Cursor<'a> { + /// Creates a cursor referencing a static empty TokenStream. + pub fn empty() -> Self { + // It's safe in this situation for us to put an `Entry` object in global + // storage, despite it not actually being safe to send across threads + // (`Ident` is a reference into a thread-local table). This is because + // this entry never includes a `Ident` object. + // + // This wrapper struct allows us to break the rules and put a `Sync` + // object in global storage. + struct UnsafeSyncEntry(Entry); + unsafe impl Sync for UnsafeSyncEntry {} + static EMPTY_ENTRY: UnsafeSyncEntry = UnsafeSyncEntry(Entry::End(0 as *const Entry)); + + Cursor { + ptr: &EMPTY_ENTRY.0, + scope: &EMPTY_ENTRY.0, + marker: PhantomData, + } + } + + /// This create method intelligently exits non-explicitly-entered + /// `None`-delimited scopes when the cursor reaches the end of them, + /// allowing for them to be treated transparently. + unsafe fn create(mut ptr: *const Entry, scope: *const Entry) -> Self { + // NOTE: If we're looking at a `End(..)`, we want to advance the cursor + // past it, unless `ptr == scope`, which means that we're at the edge of + // our cursor's scope. We should only have `ptr != scope` at the exit + // from None-delimited groups entered with `ignore_none`. + while let Entry::End(exit) = *ptr { + if ptr == scope { + break; + } + ptr = exit; + } + + Cursor { + ptr, + scope, + marker: PhantomData, + } + } + + /// Get the current entry. + fn entry(self) -> &'a Entry { + unsafe { &*self.ptr } + } + + /// Bump the cursor to point at the next token after the current one. This + /// is undefined behavior if the cursor is currently looking at an + /// `Entry::End`. + unsafe fn bump(self) -> Cursor<'a> { + Cursor::create(self.ptr.offset(1), self.scope) + } + + /// While the cursor is looking at a `None`-delimited group, move it to look + /// at the first token inside instead. If the group is empty, this will move + /// the cursor past the `None`-delimited group. + /// + /// WARNING: This mutates its argument. + fn ignore_none(&mut self) { + while let Entry::Group(group, buf) = self.entry() { + if group.delimiter() == Delimiter::None { + // NOTE: We call `Cursor::create` here to make sure that + // situations where we should immediately exit the span after + // entering it are handled correctly. + unsafe { + *self = Cursor::create(&buf.data[0], self.scope); + } + } else { + break; + } + } + } + + /// Checks whether the cursor is currently pointing at the end of its valid + /// scope. + pub fn eof(self) -> bool { + // We're at eof if we're at the end of our scope. + self.ptr == self.scope + } + + /// If the cursor is pointing at a `Group` with the given delimiter, returns + /// a cursor into that group and one pointing to the next `TokenTree`. + pub fn group(mut self, delim: Delimiter) -> Option<(Cursor<'a>, Span, Cursor<'a>)> { + // If we're not trying to enter a none-delimited group, we want to + // ignore them. We have to make sure to _not_ ignore them when we want + // to enter them, of course. For obvious reasons. + if delim != Delimiter::None { + self.ignore_none(); + } + + if let Entry::Group(group, buf) = self.entry() { + if group.delimiter() == delim { + return Some((buf.begin(), group.span(), unsafe { self.bump() })); + } + } + + None + } + + /// If the cursor is pointing at a `Ident`, returns it along with a cursor + /// pointing at the next `TokenTree`. + pub fn ident(mut self) -> Option<(Ident, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Ident(ident) => Some((ident.clone(), unsafe { self.bump() })), + _ => None, + } + } + + /// If the cursor is pointing at an `Punct`, returns it along with a cursor + /// pointing at the next `TokenTree`. + pub fn punct(mut self) -> Option<(Punct, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Punct(op) if op.as_char() != '\'' => Some((op.clone(), unsafe { self.bump() })), + _ => None, + } + } + + /// If the cursor is pointing at a `Literal`, return it along with a cursor + /// pointing at the next `TokenTree`. + pub fn literal(mut self) -> Option<(Literal, Cursor<'a>)> { + self.ignore_none(); + match self.entry() { + Entry::Literal(lit) => Some((lit.clone(), unsafe { self.bump() })), + _ => None, + } + } + + /// Copies all remaining tokens visible from this cursor into a + /// `TokenStream`. + pub fn token_stream(self) -> TokenStream { + let mut tts = Vec::new(); + let mut cursor = self; + while let Some((tt, rest)) = cursor.token_tree() { + tts.push(tt); + cursor = rest; + } + tts.into_iter().collect() + } + + /// If the cursor is pointing at a `TokenTree`, returns it along with a + /// cursor pointing at the next `TokenTree`. + /// + /// Returns `None` if the cursor has reached the end of its stream. + /// + /// This method does not treat `None`-delimited groups as transparent, and + /// will return a `Group(None, ..)` if the cursor is looking at one. + pub fn token_tree(self) -> Option<(TokenTree, Cursor<'a>)> { + let tree = match self.entry() { + Entry::Group(group, _) => group.clone().into(), + Entry::Literal(lit) => lit.clone().into(), + Entry::Ident(ident) => ident.clone().into(), + Entry::Punct(op) => op.clone().into(), + Entry::End(..) => { + return None; + } + }; + + Some((tree, unsafe { self.bump() })) + } + + /// Returns the `Span` of the current token, or `Span::call_site()` if this + /// cursor points to eof. + pub fn span(self) -> Span { + match self.entry() { + Entry::Group(group, _) => group.span(), + Entry::Literal(l) => l.span(), + Entry::Ident(t) => t.span(), + Entry::Punct(o) => o.span(), + Entry::End(..) => Span::call_site(), + } + } +} + +impl<'a> Copy for Cursor<'a> {} + +impl<'a> Clone for Cursor<'a> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a> Eq for Cursor<'a> {} + +impl<'a> PartialEq for Cursor<'a> { + fn eq(&self, other: &Self) -> bool { + let Cursor { ptr, scope, marker } = self; + let _ = marker; + *ptr == other.ptr && *scope == other.scope + } +} diff --git a/rust/macros/syn/mod.rs b/rust/macros/syn/mod.rs index 7f053eed8b1b6b..f7afda5a2ce4fe 100644 --- a/rust/macros/syn/mod.rs +++ b/rust/macros/syn/mod.rs @@ -6,6 +6,7 @@ #![allow(dead_code)] +pub mod buffer; mod lit; pub use lit::{Lit, LitByteStr, LitStr}; From c677a79e5e271bbbf1799d12ef7c6a9a19338513 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 17 May 2021 22:04:17 +0100 Subject: [PATCH 5/9] rust: `module!` macro cleanup using buffer Make use of `syn` buffer's lookahead capability to remove `try_*` parsing functions. Signed-off-by: Gary Guo --- rust/macros/module.rs | 220 ++++++++++++++++++++---------------------- 1 file changed, 103 insertions(+), 117 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 247ef5e8d1e8eb..cef45118e39d96 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -5,70 +5,55 @@ #![deny(clippy::perf)] #![deny(clippy::style)] -use crate::syn::Lit; -use proc_macro::{token_stream, Delimiter, Group, Literal, TokenStream, TokenTree}; - -fn try_ident(it: &mut token_stream::IntoIter) -> Option { - if let Some(TokenTree::Ident(ident)) = it.next() { - Some(ident.to_string()) - } else { - None - } -} - -fn try_literal(it: &mut token_stream::IntoIter) -> Option { - if let Some(TokenTree::Literal(literal)) = it.next() { - Some(literal) - } else { - None - } -} - -fn try_string(it: &mut token_stream::IntoIter) -> Option { - try_literal(it).and_then(|literal| match Lit::new(literal) { - Lit::Str(s) => { - assert!(s.suffix().is_empty(), "Unexpected suffix"); - Some(s.value()) - } - _ => None, - }) +use crate::syn::{ + buffer::{Cursor, TokenBuffer}, + Lit, +}; +use proc_macro::{Delimiter, Literal, TokenStream}; + +fn expect_ident(it: &mut Cursor<'_>) -> String { + let (ident, next) = it.ident().expect("Expected Ident"); + *it = next; + ident.to_string() } -fn expect_ident(it: &mut token_stream::IntoIter) -> String { - try_ident(it).expect("Expected Ident") +fn expect_punct(it: &mut Cursor<'_>) -> char { + let (punct, next) = it.punct().expect("Expected Punct"); + *it = next; + punct.as_char() } -fn expect_punct(it: &mut token_stream::IntoIter) -> char { - if let TokenTree::Punct(punct) = it.next().expect("Reached end of token stream for Punct") { - punct.as_char() - } else { - panic!("Expected Punct"); - } +fn expect_literal(it: &mut Cursor<'_>) -> Literal { + let (lit, next) = it.literal().expect("Expected Literal"); + *it = next; + lit } -fn expect_literal(it: &mut token_stream::IntoIter) -> Literal { - try_literal(it).expect("Expected Literal") +fn expect_group<'a>(it: &mut Cursor<'a>, delim: Delimiter) -> Cursor<'a> { + let (inner, _, next) = it.group(delim).expect("Expected Group"); + *it = next; + inner } -fn expect_group(it: &mut token_stream::IntoIter) -> Group { - if let TokenTree::Group(group) = it.next().expect("Reached end of token stream for Group") { - group - } else { - panic!("Expected Group"); +fn expect_string(it: &mut Cursor<'_>) -> String { + let lit = expect_literal(it); + let lit = Lit::new(lit); + match &lit { + Lit::Str(s) => { + assert!(s.suffix().is_empty(), "Unexpected suffix"); + s.value() + } + _ => panic!("Expected string"), } } -fn expect_string(it: &mut token_stream::IntoIter) -> String { - try_string(it).expect("Expected string") -} - #[derive(Clone, PartialEq)] enum ParamType { Ident(String), Array { vals: String, max_length: usize }, } -fn expect_array_fields(it: &mut token_stream::IntoIter) -> ParamType { +fn expect_array_fields(it: &mut Cursor<'_>) -> ParamType { assert_eq!(expect_punct(it), '<'); let vals = expect_ident(it); assert_eq!(expect_punct(it), ','); @@ -80,27 +65,39 @@ fn expect_array_fields(it: &mut token_stream::IntoIter) -> ParamType { ParamType::Array { vals, max_length } } -fn expect_type(it: &mut token_stream::IntoIter) -> ParamType { - if let TokenTree::Ident(ident) = it - .next() - .expect("Reached end of token stream for param type") - { - match ident.to_string().as_ref() { - "ArrayParam" => expect_array_fields(it), - _ => ParamType::Ident(ident.to_string()), - } - } else { - panic!("Expected Param Type") +fn expect_type(it: &mut Cursor<'_>) -> ParamType { + let (ident, next) = it.ident().expect("Expected Param Type"); + *it = next; + match ident.to_string().as_ref() { + "ArrayParam" => expect_array_fields(it), + _ => ParamType::Ident(ident.to_string()), } } -fn expect_end(it: &mut token_stream::IntoIter) { - if it.next().is_some() { - panic!("Expected end"); +fn expect_end(it: &mut Cursor<'_>) { + assert!(it.eof(), "Expected end"); +} + +fn parse_list( + it: &mut Cursor<'_>, + delim: Delimiter, + f: impl Fn(&mut Cursor<'_>) -> T, +) -> Vec { + let mut inner = expect_group(it, delim); + let mut vec = Vec::new(); + while !inner.eof() { + let item = f(&mut inner); + vec.push(item); + if inner.eof() { + break; + } + assert_eq!(expect_punct(&mut inner), ','); } + assert!(inner.eof(), "Expected end"); + vec } -fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { +fn get_literal(it: &mut Cursor<'_>, expected_name: &str) -> String { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); let literal = expect_literal(it).to_string(); @@ -108,7 +105,7 @@ fn get_literal(it: &mut token_stream::IntoIter, expected_name: &str) -> String { literal } -fn get_string(it: &mut token_stream::IntoIter, expected_name: &str) -> String { +fn get_string(it: &mut Cursor<'_>, expected_name: &str) -> String { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); let byte_string = expect_string(it); @@ -237,53 +234,45 @@ fn param_ops_path(param_type: &str) -> &'static str { } } -fn try_simple_param_val( - param_type: &str, -) -> Box Option> { +fn expect_simple_param_val(param_type: &str) -> Box) -> String> { match param_type { - "bool" => Box::new(|param_it| try_ident(param_it)), + "bool" => Box::new(|param_it| { + let (ident, next) = param_it.ident().expect("Expected ident"); + *param_it = next; + ident.to_string() + }), "str" => Box::new(|param_it| { - try_string(param_it).map(|s| { - format!( - "kernel::module_param::StringParam::Ref({})", - Literal::byte_string(s.as_bytes()) - ) - }) + let s = expect_string(param_it); + format!( + "kernel::module_param::StringParam::Ref({})", + Literal::byte_string(s.as_bytes()) + ) + }), + _ => Box::new(|param_it| { + let (lit, next) = param_it.literal().expect("Expected literal"); + *param_it = next; + lit.to_string() }), - _ => Box::new(|param_it| try_literal(param_it).map(|x| x.to_string())), } } -fn get_default(param_type: &ParamType, param_it: &mut token_stream::IntoIter) -> String { - let try_param_val = match param_type { +fn get_default(param_type: &ParamType, param_it: &mut Cursor<'_>) -> String { + let expect_param_val = match param_type { ParamType::Ident(ref param_type) | ParamType::Array { vals: ref param_type, max_length: _, - } => try_simple_param_val(param_type), + } => expect_simple_param_val(param_type), }; assert_eq!(expect_ident(param_it), "default"); assert_eq!(expect_punct(param_it), ':'); let default = match param_type { - ParamType::Ident(_) => try_param_val(param_it).expect("Expected default param value"), + ParamType::Ident(_) => expect_param_val(param_it), ParamType::Array { vals: _, max_length: _, } => { - let group = expect_group(param_it); - assert_eq!(group.delimiter(), Delimiter::Bracket); - let mut default_vals = Vec::new(); - let mut it = group.stream().into_iter(); - - while let Some(default_val) = try_param_val(&mut it) { - default_vals.push(default_val); - match it.next() { - Some(TokenTree::Punct(punct)) => assert_eq!(punct.as_char(), ','), - None => break, - _ => panic!("Expected ',' or end of array default values"), - } - } - + let default_vals = parse_list(param_it, Delimiter::Bracket, expect_param_val); let mut default_array = "kernel::module_param::ArrayParam::create(&[".to_string(); default_array.push_str( &default_vals @@ -308,19 +297,19 @@ fn generated_array_ops_name(vals: &str, max_length: usize) -> String { ) } -#[derive(Debug, Default)] -struct ModuleInfo { +#[derive(Default)] +struct ModuleInfo<'a> { type_: String, license: String, name: String, author: Option, description: Option, alias: Option, - params: Option, + params: Option>, } -impl ModuleInfo { - fn parse(it: &mut token_stream::IntoIter) -> Self { +impl<'a> ModuleInfo<'a> { + fn parse(it: &mut Cursor<'a>) -> Self { let mut info = ModuleInfo::default(); const EXPECTED_KEYS: &[&str] = &[ @@ -337,11 +326,11 @@ impl ModuleInfo { let mut seen_keys = Vec::new(); loop { - let key = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; + if it.eof() { + break; + } + + let key = expect_ident(it); if seen_keys.contains(&key) { panic!( @@ -360,7 +349,7 @@ impl ModuleInfo { "license" => info.license = expect_string(it), "alias" => info.alias = Some(expect_string(it)), "alias_rtnl_link" => info.alias = Some(format!("rtnl-link-{}", expect_string(it))), - "params" => info.params = Some(expect_group(it)), + "params" => info.params = Some(expect_group(it, Delimiter::Brace)), _ => panic!( "Unknown key \"{}\". Valid keys are: {:?}.", key, EXPECTED_KEYS @@ -399,7 +388,8 @@ impl ModuleInfo { } pub fn module(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); + let buffer = TokenBuffer::new(ts); + let mut it = buffer.begin(); let info = ModuleInfo::parse(&mut it); @@ -408,25 +398,20 @@ pub fn module(ts: TokenStream) -> TokenStream { let mut array_types_to_generate = Vec::new(); let mut params_modinfo = String::new(); if let Some(params) = info.params { - assert_eq!(params.delimiter(), Delimiter::Brace); - - let mut it = params.stream().into_iter(); + let mut it = params; loop { - let param_name = match it.next() { - Some(TokenTree::Ident(ident)) => ident.to_string(), - Some(_) => panic!("Expected Ident or end"), - None => break, - }; + if it.eof() { + break; + } + + let param_name = expect_ident(&mut it); assert_eq!(expect_punct(&mut it), ':'); let param_type = expect_type(&mut it); - let group = expect_group(&mut it); + let mut param_it = expect_group(&mut it, Delimiter::Brace); assert_eq!(expect_punct(&mut it), ','); - assert_eq!(group.delimiter(), Delimiter::Brace); - - let mut param_it = group.stream().into_iter(); let param_default = get_default(¶m_type, &mut param_it); let param_permissions = get_literal(&mut param_it, "permissions"); let param_description = get_string(&mut param_it, "description"); @@ -695,7 +680,8 @@ pub fn module(ts: TokenStream) -> TokenStream { } pub fn module_misc_device(ts: TokenStream) -> TokenStream { - let mut it = ts.into_iter(); + let buffer = TokenBuffer::new(ts); + let mut it = buffer.begin(); let info = ModuleInfo::parse(&mut it); From c10abf578c03253f1321c844cd5946970c83b1da Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 17 May 2021 22:55:01 +0100 Subject: [PATCH 6/9] rust: cleanup modinfo generation in `module!` Signed-off-by: Gary Guo --- rust/macros/module.rs | 192 ++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 108 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index cef45118e39d96..2ebfaa65e70968 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -113,91 +113,83 @@ fn get_string(it: &mut Cursor<'_>, expected_name: &str) -> String { byte_string } -fn __build_modinfo_string_base( - module: &str, - field: &str, - content: &str, - variable: &str, - builtin: bool, -) -> String { - let string = if builtin { - // Built-in modules prefix their modinfo strings by `module.`. - format!( - "{module}.{field}={content}\0", - module = module, - field = field, - content = content - ) - } else { - // Loadable modules' modinfo strings go as-is. - format!("{field}={content}\0", field = field, content = content) - }; +struct ModInfoBuilder<'a> { + module: &'a str, + counter: usize, + buffer: String, +} - format!( - " - {cfg} - #[link_section = \".modinfo\"] - #[used] - pub static {variable}: [u8; {length}] = *{string}; - ", - cfg = if builtin { - "#[cfg(not(MODULE))]" +impl<'a> ModInfoBuilder<'a> { + fn new(module: &'a str) -> Self { + ModInfoBuilder { + module, + counter: 0, + buffer: String::new(), + } + } + + fn emit_base(&mut self, field: &str, content: &str, builtin: bool) { + use std::fmt::Write; + + let string = if builtin { + // Built-in modules prefix their modinfo strings by `module.`. + format!( + "{module}.{field}={content}\0", + module = self.module, + field = field, + content = content + ) } else { - "#[cfg(MODULE)]" - }, - variable = variable, - length = string.len(), - string = Literal::byte_string(string.as_bytes()), - ) -} + // Loadable modules' modinfo strings go as-is. + format!("{field}={content}\0", field = field, content = content) + }; -fn __build_modinfo_string_variable(module: &str, field: &str) -> String { - format!("__{module}_{field}", module = module, field = field) -} + write!( + &mut self.buffer, + " + {cfg} + #[link_section = \".modinfo\"] + #[used] + pub static __{module}_{counter}: [u8; {length}] = *{string}; + ", + cfg = if builtin { + "#[cfg(not(MODULE))]" + } else { + "#[cfg(MODULE)]" + }, + module = self.module, + counter = self.counter, + length = string.len(), + string = Literal::byte_string(string.as_bytes()), + ) + .unwrap(); -fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String { - __build_modinfo_string_base( - module, - field, - content, - &__build_modinfo_string_variable(module, field), - true, - ) -} + self.counter += 1; + } -fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String { - __build_modinfo_string_base( - module, - field, - content, - &__build_modinfo_string_variable(module, field), - false, - ) -} + fn emit_only_builtin(&mut self, field: &str, content: &str) { + self.emit_base(field, content, true) + } -fn build_modinfo_string(module: &str, field: &str, content: &str) -> String { - build_modinfo_string_only_builtin(module, field, content) - + &build_modinfo_string_only_loadable(module, field, content) -} + fn emit_only_loadable(&mut self, field: &str, content: &str) { + self.emit_base(field, content, false) + } -fn build_modinfo_string_optional(module: &str, field: &str, content: Option<&str>) -> String { - if let Some(content) = content { - build_modinfo_string(module, field, content) - } else { - "".to_string() + fn emit(&mut self, field: &str, content: &str) { + self.emit_only_builtin(field, content); + self.emit_only_loadable(field, content); } -} -fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String { - let variable = format!( - "__{module}_{field}_{param}", - module = module, - field = field, - param = param - ); - let content = format!("{param}:{content}", param = param, content = content); - __build_modinfo_string_base(module, field, &content, &variable, true) - + &__build_modinfo_string_base(module, field, &content, &variable, false) + fn emit_optional(&mut self, field: &str, content: Option<&str>) { + if let Some(content) = content { + self.emit(field, content); + } + } + + fn emit_param(&mut self, field: &str, param: &str, content: &str) { + let content = format!("{param}:{content}", param = param, content = content); + self.emit(field, &content); + } } fn permissions_are_readonly(perms: &str) -> bool { @@ -395,8 +387,18 @@ pub fn module(ts: TokenStream) -> TokenStream { let name = info.name.clone(); + let mut modinfo = ModInfoBuilder::new(&name); + modinfo.emit_optional("author", info.author.as_deref()); + modinfo.emit_optional("description", info.description.as_deref()); + modinfo.emit("license", &info.license); + modinfo.emit_optional("alias", info.alias.as_deref()); + + // Built-in modules also export the `file` modinfo string + let file = + std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable"); + modinfo.emit_only_builtin("file", &file); + let mut array_types_to_generate = Vec::new(); - let mut params_modinfo = String::new(); if let Some(params) = info.params { let mut it = params; @@ -436,18 +438,8 @@ pub fn module(ts: TokenStream) -> TokenStream { } }; - params_modinfo.push_str(&build_modinfo_string_param( - &name, - "parmtype", - ¶m_name, - ¶m_kernel_type, - )); - params_modinfo.push_str(&build_modinfo_string_param( - &name, - "parm", - ¶m_name, - ¶m_description, - )); + modinfo.emit_param("parmtype", ¶m_name, ¶m_kernel_type); + modinfo.emit_param("parm", ¶m_name, ¶m_description); let param_type_internal = match param_type { ParamType::Ident(ref param_type) => match param_type.as_ref() { "str" => "kernel::module_param::StringParam".to_string(), @@ -496,7 +488,7 @@ pub fn module(ts: TokenStream) -> TokenStream { name = name, param_name = param_name, ); - params_modinfo.push_str( + modinfo.buffer.push_str( &format!( " static mut __{name}_{param_name}_value: {param_type_internal} = {param_default}; @@ -572,9 +564,6 @@ pub fn module(ts: TokenStream) -> TokenStream { )); } - let file = - std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable"); - format!( " /// The module name. @@ -654,26 +643,13 @@ pub fn module(ts: TokenStream) -> TokenStream { }} }} - {author} - {description} - {license} - {alias} - - // Built-in modules also export the `file` modinfo string - {file} - - {params_modinfo} + {modinfo} {generated_array_types} ", type_ = info.type_, name = info.name, - author = &build_modinfo_string_optional(&name, "author", info.author.as_deref()), - description = &build_modinfo_string_optional(&name, "description", info.description.as_deref()), - license = &build_modinfo_string(&name, "license", &info.license), - alias = &build_modinfo_string_optional(&name, "alias", info.alias.as_deref()), - file = &build_modinfo_string_only_builtin(&name, "file", &file), - params_modinfo = params_modinfo, + modinfo = modinfo.buffer, generated_array_types = generated_array_types, initcall_section = ".initcall6.init" ).parse().expect("Error parsing formatted string into token stream.") From 55989044cd94ed5106f6b5d408f8cf2625263080 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 18 May 2021 22:32:10 +0100 Subject: [PATCH 7/9] rust: separate parse and codegen in `module!` Doing so allows `module_misc_device!` to do codegen directly instead of generating a `module!` invocation. Generating `module!` invocation is error-prone and would require significant effort when features are added to `module!` macro or when syntax is changed. Signed-off-by: Gary Guo --- rust/macros/module.rs | 374 ++++++++++++++++++++---------------------- 1 file changed, 179 insertions(+), 195 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 2ebfaa65e70968..4773c9ab0b7932 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -97,10 +97,10 @@ fn parse_list( vec } -fn get_literal(it: &mut Cursor<'_>, expected_name: &str) -> String { +fn get_literal(it: &mut Cursor<'_>, expected_name: &str) -> Literal { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); - let literal = expect_literal(it).to_string(); + let literal = expect_literal(it); assert_eq!(expect_punct(it), ','); literal } @@ -192,20 +192,8 @@ impl<'a> ModInfoBuilder<'a> { } } -fn permissions_are_readonly(perms: &str) -> bool { - let (radix, digits) = if let Some(n) = perms.strip_prefix("0x") { - (16, n) - } else if let Some(n) = perms.strip_prefix("0o") { - (8, n) - } else if let Some(n) = perms.strip_prefix("0b") { - (2, n) - } else { - (10, perms) - }; - match u32::from_str_radix(digits, radix) { - Ok(perms) => perms & 0o222 == 0, - Err(_) => false, - } +fn permissions_are_readonly(perms: u32) -> bool { + perms & 0o222 == 0 } fn param_ops_path(param_type: &str) -> &'static str { @@ -289,19 +277,53 @@ fn generated_array_ops_name(vals: &str, max_length: usize) -> String { ) } +struct ParamInfo { + name: String, + type_: ParamType, + default: String, + permission: u32, + description: String, +} + +impl ParamInfo { + fn parse(it: &mut Cursor<'_>) -> Self { + let param_name = expect_ident(it); + + assert_eq!(expect_punct(it), ':'); + let param_type = expect_type(it); + let mut param_it = expect_group(it, Delimiter::Brace); + + let param_default = get_default(¶m_type, &mut param_it); + let param_permissions = match Lit::new(get_literal(&mut param_it, "permissions")) { + Lit::Int(i) => i.base10_digits().parse::().unwrap(), + _ => panic!("Permission is expected to be an integer literal"), + }; + let param_description = get_string(&mut param_it, "description"); + expect_end(&mut param_it); + + ParamInfo { + name: param_name, + type_: param_type, + default: param_default, + permission: param_permissions, + description: param_description, + } + } +} + #[derive(Default)] -struct ModuleInfo<'a> { +struct ModuleInfo { type_: String, license: String, name: String, author: Option, description: Option, alias: Option, - params: Option>, + params: Vec, } -impl<'a> ModuleInfo<'a> { - fn parse(it: &mut Cursor<'a>) -> Self { +impl ModuleInfo { + fn parse(it: &mut Cursor<'_>) -> Self { let mut info = ModuleInfo::default(); const EXPECTED_KEYS: &[&str] = &[ @@ -341,7 +363,7 @@ impl<'a> ModuleInfo<'a> { "license" => info.license = expect_string(it), "alias" => info.alias = Some(expect_string(it)), "alias_rtnl_link" => info.alias = Some(format!("rtnl-link-{}", expect_string(it))), - "params" => info.params = Some(expect_group(it, Delimiter::Brace)), + "params" => info.params = parse_list(it, Delimiter::Brace, ParamInfo::parse), _ => panic!( "Unknown key \"{}\". Valid keys are: {:?}.", key, EXPECTED_KEYS @@ -377,51 +399,24 @@ impl<'a> ModuleInfo<'a> { info } -} -pub fn module(ts: TokenStream) -> TokenStream { - let buffer = TokenBuffer::new(ts); - let mut it = buffer.begin(); - - let info = ModuleInfo::parse(&mut it); + fn generate(&self) -> TokenStream { + let mut modinfo = ModInfoBuilder::new(&self.name); + modinfo.emit_optional("author", self.author.as_deref()); + modinfo.emit_optional("description", self.description.as_deref()); + modinfo.emit("license", &self.license); + modinfo.emit_optional("alias", self.alias.as_deref()); - let name = info.name.clone(); - - let mut modinfo = ModInfoBuilder::new(&name); - modinfo.emit_optional("author", info.author.as_deref()); - modinfo.emit_optional("description", info.description.as_deref()); - modinfo.emit("license", &info.license); - modinfo.emit_optional("alias", info.alias.as_deref()); - - // Built-in modules also export the `file` modinfo string - let file = - std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable"); - modinfo.emit_only_builtin("file", &file); - - let mut array_types_to_generate = Vec::new(); - if let Some(params) = info.params { - let mut it = params; - - loop { - if it.eof() { - break; - } - - let param_name = expect_ident(&mut it); - - assert_eq!(expect_punct(&mut it), ':'); - let param_type = expect_type(&mut it); - let mut param_it = expect_group(&mut it, Delimiter::Brace); - assert_eq!(expect_punct(&mut it), ','); - - let param_default = get_default(¶m_type, &mut param_it); - let param_permissions = get_literal(&mut param_it, "permissions"); - let param_description = get_string(&mut param_it, "description"); - expect_end(&mut param_it); + // Built-in modules also export the `file` modinfo string + let file = std::env::var("RUST_MODFILE") + .expect("Unable to fetch RUST_MODFILE environmental variable"); + modinfo.emit_only_builtin("file", &file); + let mut array_types_to_generate = Vec::new(); + for param in self.params.iter() { // TODO: more primitive types // TODO: other kinds: unsafes, etc. - let (param_kernel_type, ops): (String, _) = match param_type { + let (param_kernel_type, ops): (String, _) = match param.type_ { ParamType::Ident(ref param_type) => ( param_type.to_string(), param_ops_path(¶m_type).to_string(), @@ -438,9 +433,9 @@ pub fn module(ts: TokenStream) -> TokenStream { } }; - modinfo.emit_param("parmtype", ¶m_name, ¶m_kernel_type); - modinfo.emit_param("parm", ¶m_name, ¶m_description); - let param_type_internal = match param_type { + modinfo.emit_param("parmtype", ¶m.name, ¶m_kernel_type); + modinfo.emit_param("parm", ¶m.name, ¶m.description); + let param_type_internal = match param.type_ { ParamType::Ident(ref param_type) => match param_type.as_ref() { "str" => "kernel::module_param::StringParam".to_string(), other => other.to_string(), @@ -454,7 +449,7 @@ pub fn module(ts: TokenStream) -> TokenStream { max_length = max_length ), }; - let read_func = if permissions_are_readonly(¶m_permissions) { + let read_func = if permissions_are_readonly(param.permission) { format!( " fn read(&self) -> &<{param_type_internal} as kernel::module_param::ModuleParam>::Value {{ @@ -462,8 +457,8 @@ pub fn module(ts: TokenStream) -> TokenStream { unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} }} ", - name = name, - param_name = param_name, + name = self.name, + param_name = param.name, param_type_internal = param_type_internal, ) } else { @@ -474,8 +469,8 @@ pub fn module(ts: TokenStream) -> TokenStream { unsafe {{ <{param_type_internal} as kernel::module_param::ModuleParam>::value(&__{name}_{param_name}_value) }} }} ", - name = name, - param_name = param_name, + name = self.name, + param_name = param.name, param_type_internal = param_type_internal, ) }; @@ -485,8 +480,8 @@ pub fn module(ts: TokenStream) -> TokenStream { arg: unsafe {{ &__{name}_{param_name}_value }} as *const _ as *mut kernel::c_types::c_void, }}, ", - name = name, - param_name = param_name, + name = self.name, + param_name = param.name, ); modinfo.buffer.push_str( &format!( @@ -534,136 +529,145 @@ pub fn module(ts: TokenStream) -> TokenStream { __bindgen_anon_1: {kparam} }}); ", - name = name, + name = self.name, param_type_internal = param_type_internal, read_func = read_func, - param_default = param_default, - param_name = param_name, + param_default = param.default, + param_name = param.name, ops = ops, - permissions = param_permissions, + permissions = param.permission, kparam = kparam, ) ); } - } - let mut generated_array_types = String::new(); + let mut generated_array_types = String::new(); - for (vals, max_length) in array_types_to_generate { - let ops_name = generated_array_ops_name(&vals, max_length); - generated_array_types.push_str(&format!( - " - kernel::make_param_ops!( - {ops_name}, - kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> - ); - ", - ops_name = ops_name, - vals = vals, - max_length = max_length, - )); - } + for (vals, max_length) in array_types_to_generate { + let ops_name = generated_array_ops_name(&vals, max_length); + generated_array_types.push_str(&format!( + " + kernel::make_param_ops!( + {ops_name}, + kernel::module_param::ArrayParam<{vals}, {{ {max_length} }}> + ); + ", + ops_name = ops_name, + vals = vals, + max_length = max_length, + )); + } - format!( - " - /// The module name. - /// - /// Used by the printing macros, e.g. [`info!`]. - const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; - - static mut __MOD: Option<{type_}> = None; - - // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. - #[cfg(MODULE)] - static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _) }}; - #[cfg(not(MODULE))] - static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(core::ptr::null_mut()) }}; - - // Loadable modules need to export the `{{init,cleanup}}_module` identifiers - #[cfg(MODULE)] - #[no_mangle] - pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{ - __init() - }} + format!( + " + /// The module name. + /// + /// Used by the printing macros, e.g. [`info!`]. + const __LOG_PREFIX: &[u8] = b\"{name}\\0\"; + + static mut __MOD: Option<{type_}> = None; + + // SAFETY: `__this_module` is constructed by the kernel at load time and will not be freed until the module is unloaded. + #[cfg(MODULE)] + static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(&kernel::bindings::__this_module as *const _ as *mut _) }}; + #[cfg(not(MODULE))] + static THIS_MODULE: kernel::ThisModule = unsafe {{ kernel::ThisModule::from_ptr(core::ptr::null_mut()) }}; + + // Loadable modules need to export the `{{init,cleanup}}_module` identifiers + #[cfg(MODULE)] + #[no_mangle] + pub extern \"C\" fn init_module() -> kernel::c_types::c_int {{ + __init() + }} - #[cfg(MODULE)] - #[no_mangle] - pub extern \"C\" fn cleanup_module() {{ - __exit() - }} + #[cfg(MODULE)] + #[no_mangle] + pub extern \"C\" fn cleanup_module() {{ + __exit() + }} - // Built-in modules are initialized through an initcall pointer - // and the identifiers need to be unique - #[cfg(not(MODULE))] - #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] - #[link_section = \"{initcall_section}\"] - #[used] - pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; - - #[cfg(not(MODULE))] - #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] - global_asm!( - r#\".section \"{initcall_section}\", \"a\" - __{name}_initcall: - .long __{name}_init - . - .previous - \"# - ); + // Built-in modules are initialized through an initcall pointer + // and the identifiers need to be unique + #[cfg(not(MODULE))] + #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] + #[link_section = \"{initcall_section}\"] + #[used] + pub static __{name}_initcall: extern \"C\" fn() -> kernel::c_types::c_int = __{name}_init; + + #[cfg(not(MODULE))] + #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] + global_asm!( + r#\".section \"{initcall_section}\", \"a\" + __{name}_initcall: + .long __{name}_init - . + .previous + \"# + ); - #[cfg(not(MODULE))] - #[no_mangle] - pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{ - __init() - }} + #[cfg(not(MODULE))] + #[no_mangle] + pub extern \"C\" fn __{name}_init() -> kernel::c_types::c_int {{ + __init() + }} - #[cfg(not(MODULE))] - #[no_mangle] - pub extern \"C\" fn __{name}_exit() {{ - __exit() - }} + #[cfg(not(MODULE))] + #[no_mangle] + pub extern \"C\" fn __{name}_exit() {{ + __exit() + }} - fn __init() -> kernel::c_types::c_int {{ - match <{type_} as kernel::KernelModule>::init() {{ - Ok(m) => {{ - unsafe {{ - __MOD = Some(m); + fn __init() -> kernel::c_types::c_int {{ + match <{type_} as kernel::KernelModule>::init() {{ + Ok(m) => {{ + unsafe {{ + __MOD = Some(m); + }} + return 0; + }} + Err(e) => {{ + return e.to_kernel_errno(); }} - return 0; - }} - Err(e) => {{ - return e.to_kernel_errno(); }} }} - }} - fn __exit() {{ - unsafe {{ - // Invokes `drop()` on `__MOD`, which should be used for cleanup. - __MOD = None; + fn __exit() {{ + unsafe {{ + // Invokes `drop()` on `__MOD`, which should be used for cleanup. + __MOD = None; + }} }} - }} - {modinfo} + {modinfo} - {generated_array_types} - ", - type_ = info.type_, - name = info.name, - modinfo = modinfo.buffer, - generated_array_types = generated_array_types, - initcall_section = ".initcall6.init" - ).parse().expect("Error parsing formatted string into token stream.") + {generated_array_types} + ", + type_ = self.type_, + name = self.name, + modinfo = modinfo.buffer, + generated_array_types = generated_array_types, + initcall_section = ".initcall6.init" + ).parse().expect("Error parsing formatted string into token stream.") + } } -pub fn module_misc_device(ts: TokenStream) -> TokenStream { +pub fn module(ts: TokenStream) -> TokenStream { let buffer = TokenBuffer::new(ts); let mut it = buffer.begin(); let info = ModuleInfo::parse(&mut it); + info.generate() +} - let module = format!("__internal_ModuleFor{}", info.type_); +pub fn module_misc_device(ts: TokenStream) -> TokenStream { + let buffer = TokenBuffer::new(ts); + let mut it = buffer.begin(); - format!( + let mut info = ModuleInfo::parse(&mut it); + let type_ = info.type_; + let module = format!("__internal_ModuleFor{}", type_); + info.type_ = module.clone(); + + let extra = format!( " #[doc(hidden)] struct {module} {{ @@ -681,33 +685,13 @@ pub fn module_misc_device(ts: TokenStream) -> TokenStream { }}) }} }} - - kernel::prelude::module! {{ - type: {module}, - name: {name}, - {author} - {description} - license: {license}, - {alias} - }} ", module = module, - type_ = info.type_, + type_ = type_, name = Literal::string(&info.name), - author = info - .author - .map(|v| format!("author: {},", Literal::string(&v))) - .unwrap_or_else(|| "".to_string()), - description = info - .description - .map(|v| format!("description: {},", Literal::string(&v))) - .unwrap_or_else(|| "".to_string()), - alias = info - .alias - .map(|v| format!("alias: {},", Literal::string(&v))) - .unwrap_or_else(|| "".to_string()), - license = Literal::string(&info.license) ) .parse() - .expect("Error parsing formatted string into token stream.") + .expect("Error parsing formatted string into token stream."); + + vec![extra, info.generate()].into_iter().collect() } From 38e0f28f73c28a91fb93f12bbcadeb2c8f3cc057 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 18 May 2021 22:35:25 +0100 Subject: [PATCH 8/9] rust: multiple author/alias support in `module!` Signed-off-by: Gary Guo --- rust/macros/module.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 4773c9ab0b7932..6c97afb470fffd 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -97,6 +97,18 @@ fn parse_list( vec } +fn parse_item_or_list( + it: &mut Cursor<'_>, + delim: Delimiter, + f: impl Fn(&mut Cursor<'_>) -> T, +) -> Vec { + if it.group(delim).is_some() { + parse_list(it, delim, f) + } else { + vec![f(it)] + } +} + fn get_literal(it: &mut Cursor<'_>, expected_name: &str) -> Literal { assert_eq!(expect_ident(it), expected_name); assert_eq!(expect_punct(it), ':'); @@ -316,9 +328,9 @@ struct ModuleInfo { type_: String, license: String, name: String, - author: Option, + author: Vec, description: Option, - alias: Option, + alias: Vec, params: Vec, } @@ -358,11 +370,16 @@ impl ModuleInfo { match key.as_str() { "type" => info.type_ = expect_ident(it), "name" => info.name = expect_string(it), - "author" => info.author = Some(expect_string(it)), + "author" => info.author = parse_item_or_list(it, Delimiter::Bracket, expect_string), "description" => info.description = Some(expect_string(it)), "license" => info.license = expect_string(it), - "alias" => info.alias = Some(expect_string(it)), - "alias_rtnl_link" => info.alias = Some(format!("rtnl-link-{}", expect_string(it))), + "alias" => info.alias = parse_item_or_list(it, Delimiter::Bracket, expect_string), + "alias_rtnl_link" => { + info.alias = parse_item_or_list(it, Delimiter::Bracket, expect_string) + .into_iter() + .map(|x| format!("rtnl-link-{}", x)) + .collect() + } "params" => info.params = parse_list(it, Delimiter::Brace, ParamInfo::parse), _ => panic!( "Unknown key \"{}\". Valid keys are: {:?}.", @@ -402,10 +419,14 @@ impl ModuleInfo { fn generate(&self) -> TokenStream { let mut modinfo = ModInfoBuilder::new(&self.name); - modinfo.emit_optional("author", self.author.as_deref()); + for author in self.author.iter() { + modinfo.emit("author", author); + } modinfo.emit_optional("description", self.description.as_deref()); modinfo.emit("license", &self.license); - modinfo.emit_optional("alias", self.alias.as_deref()); + for alias in self.alias.iter() { + modinfo.emit("alias", alias); + } // Built-in modules also export the `file` modinfo string let file = std::env::var("RUST_MODFILE") From 6f7f4b324c0ff31c15146f6bfa94b7bd41c23202 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 19 May 2021 20:22:41 +0100 Subject: [PATCH 9/9] rust: allow any constexpr to be used as param default Signed-off-by: Gary Guo --- rust/macros/module.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 6c97afb470fffd..8a89f83fb1eb0c 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -226,13 +226,26 @@ fn param_ops_path(param_type: &str) -> &'static str { } } +fn parse_expr(it: &mut Cursor<'_>) -> TokenStream { + let mut tt = Vec::new(); + while !it.eof() { + if let Some((punct, _)) = it.punct() { + if let ',' | ';' = punct.as_char() { + break; + } + } + let (token, next) = it.token_tree().unwrap(); + tt.push(token); + *it = next; + } + if tt.is_empty() { + panic!("Expected expression"); + } + tt.into_iter().collect() +} + fn expect_simple_param_val(param_type: &str) -> Box) -> String> { match param_type { - "bool" => Box::new(|param_it| { - let (ident, next) = param_it.ident().expect("Expected ident"); - *param_it = next; - ident.to_string() - }), "str" => Box::new(|param_it| { let s = expect_string(param_it); format!( @@ -240,11 +253,7 @@ fn expect_simple_param_val(param_type: &str) -> Box) -> S Literal::byte_string(s.as_bytes()) ) }), - _ => Box::new(|param_it| { - let (lit, next) = param_it.literal().expect("Expected literal"); - *param_it = next; - lit.to_string() - }), + _ => Box::new(|param_it| parse_expr(param_it).to_string()), } }