From 3507dcd3b183b3dc67f7658d194635c0c5c4c92b Mon Sep 17 00:00:00 2001 From: Kenny Kerr Date: Wed, 15 Mar 2023 12:31:50 -0500 Subject: [PATCH] Advanced metadata filtering (#2385) --- crates/libs/bindgen/src/lib.rs | 12 +- crates/libs/implement/src/lib.rs | 2 +- crates/libs/interface/src/lib.rs | 6 +- crates/libs/metadata/src/reader/filter.rs | 157 ++++++++++++++++++ crates/libs/metadata/src/reader/mod.rs | 19 ++- .../samples/windows/spellchecker/src/main.rs | 2 +- crates/tests/implement/tests/data_object.rs | 2 +- .../tests/interface/tests/non_com_existing.rs | 2 +- crates/tests/matrix3x2/tests/matrix3x2.rs | 2 +- crates/tests/not_dll/tests/win.rs | 2 +- crates/tests/win32/tests/win32.rs | 2 +- crates/tools/gnu/src/main.rs | 2 +- crates/tools/lib/src/lib.rs | 2 +- crates/tools/msvc/src/main.rs | 2 +- crates/tools/sys/src/main.rs | 18 +- crates/tools/windows/src/main.rs | 10 +- 16 files changed, 209 insertions(+), 33 deletions(-) create mode 100644 crates/libs/metadata/src/reader/filter.rs diff --git a/crates/libs/bindgen/src/lib.rs b/crates/libs/bindgen/src/lib.rs index c76a0273b9..ec60d67d43 100644 --- a/crates/libs/bindgen/src/lib.rs +++ b/crates/libs/bindgen/src/lib.rs @@ -41,7 +41,10 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String { let mut functions = BTreeMap::<&str, TokenStream>::new(); let mut types = BTreeMap::>::new(); - for def in gen.reader.namespace_types(tree.namespace) { + for def in gen + .reader + .namespace_types(tree.namespace, &Default::default()) + { let type_name = gen.reader.type_def_type_name(def); if REMAP_TYPES.iter().any(|(x, _)| x == &type_name) { continue; @@ -145,7 +148,10 @@ pub fn namespace(gen: &Gen, tree: &Tree) -> String { pub fn namespace_impl(gen: &Gen, tree: &Tree) -> String { let mut types = BTreeMap::<&str, TokenStream>::new(); - for def in gen.reader.namespace_types(tree.namespace) { + for def in gen + .reader + .namespace_types(tree.namespace, &Default::default()) + { let type_name = gen.reader.type_def_type_name(def); if CORE_TYPES.iter().any(|(x, _)| x == &type_name) { continue; @@ -172,7 +178,7 @@ pub fn namespace_impl(gen: &Gen, tree: &Tree) -> String { pub fn component(namespace: &str, files: &[File]) -> String { let reader = &Reader::new(files); - let tree = reader.tree(namespace, &[]).expect("Namespace not found"); + let tree = reader.tree(namespace, &Default::default()); let mut gen = Gen::new(reader); gen.namespace = tree.namespace; gen.component = true; diff --git a/crates/libs/implement/src/lib.rs b/crates/libs/implement/src/lib.rs index edb6b70997..24b5475729 100644 --- a/crates/libs/implement/src/lib.rs +++ b/crates/libs/implement/src/lib.rs @@ -135,7 +135,7 @@ pub fn implement(attributes: proc_macro::TokenStream, original_type: proc_macro: let remaining = self.count.release(); if remaining == 0 { unsafe { - let _ = ::std::boxed::Box::from_raw(self as *const Self as *mut Self); + _ = ::std::boxed::Box::from_raw(self as *const Self as *mut Self); } } remaining diff --git a/crates/libs/interface/src/lib.rs b/crates/libs/interface/src/lib.rs index 4a801c33c2..d40fccf1bd 100644 --- a/crates/libs/interface/src/lib.rs +++ b/crates/libs/interface/src/lib.rs @@ -372,10 +372,10 @@ impl Parse for Interface { } let visibility = input.parse::()?; - let _ = input.parse::()?; - let _ = input.parse::()?; + _ = input.parse::()?; + _ = input.parse::()?; let name = input.parse::()?; - let _ = input.parse::(); + _ = input.parse::(); let parent = input.parse::().ok(); let content; syn::braced!(content in input); diff --git a/crates/libs/metadata/src/reader/filter.rs b/crates/libs/metadata/src/reader/filter.rs new file mode 100644 index 0000000000..4b6651fb66 --- /dev/null +++ b/crates/libs/metadata/src/reader/filter.rs @@ -0,0 +1,157 @@ +use super::*; + +#[derive(Default)] +pub struct Filter<'a>(Vec<(&'a str, bool)>); + +impl<'a> Filter<'a> { + pub fn new(include: &[&'a str], exclude: &[&'a str]) -> Self { + let mut rules = vec![]; + + for include in include { + rules.push((*include, true)); + } + + for exclude in exclude { + rules.push((*exclude, false)); + } + + rules.sort_unstable_by(|left, right| { + let left = (left.0.len(), !left.1); + let right = (right.0.len(), !right.1); + left.cmp(&right).reverse() + }); + + Self(rules) + } + + pub fn includes_namespace(&self, namespace: &str) -> bool { + if self.is_empty() { + return true; + } + + for rule in &self.0 { + if rule.1 { + // include + if rule.0.starts_with(namespace) { + return true; + } + if namespace.starts_with(rule.0) { + return true; + } + } else { + // exclude + if namespace.starts_with(rule.0) { + return false; + } + } + } + + false + } + + pub fn includes_type(&self, reader: &Reader, ty: TypeDef) -> bool { + self.includes_type_name(reader.type_def_type_name(ty)) + } + + fn includes_type_name(&self, type_name: TypeName) -> bool { + if self.is_empty() { + return true; + } + + for rule in &self.0 { + if match_type_name(rule.0, type_name.namespace, type_name.name) { + return rule.1; + } + } + + false + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +fn match_type_name(rule: &str, namespace: &str, name: &str) -> bool { + if rule.len() <= namespace.len() { + return namespace.starts_with(rule); + } + + if !rule.starts_with(namespace) { + return false; + } + + if rule.as_bytes()[namespace.len()] != b'.' { + return false; + } + + name.starts_with(&rule[namespace.len() + 1..]) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn includes_type_name(filter: &Filter, full_name: &str) -> bool { + filter.includes_type_name(TypeName::parse(full_name)) + } + + #[test] + fn test_namespace() { + let include = ["N1.N2"]; + let exclude = ["N1.N2.N3"]; + let f = Filter::new(&include, &exclude); + + assert!(f.includes_namespace("N1")); + assert!(f.includes_namespace("N1.N2")); + assert!(f.includes_namespace("N1.N2.N4")); + + assert!(!f.includes_namespace("N1.N2.N3")); + assert!(!f.includes_namespace("N1.N2.N3.N4")); + } + + #[test] + fn test_simple() { + let include = ["N1", "N3", "N3.N4.N5"]; + let exclude = ["N2", "N3.N4"]; + let f = Filter::new(&include, &exclude); + + assert!(!f.is_empty()); + + assert!(!includes_type_name(&f, "NN.T")); + + assert!(includes_type_name(&f, "N1.T")); + assert!(includes_type_name(&f, "N3.T")); + + assert!(!includes_type_name(&f, "N2.T")); + assert!(!includes_type_name(&f, "N3.N4.T")); + + assert!(includes_type_name(&f, "N3.N4.N5.T")); + } + + #[test] + fn filter_excludes_same_length() { + let include = ["N.N1", "N.N2"]; + let exclude = ["N.N3", "N.N4"]; + let f = Filter::new(&include, &exclude); + + assert!(!f.is_empty()); + + assert!(includes_type_name(&f, "N.N1.T")); + assert!(includes_type_name(&f, "N.N2.T")); + + assert!(!includes_type_name(&f, "N.N3.T")); + assert!(!includes_type_name(&f, "N.N4.T")); + } + + #[test] + fn filter_exclude_include_precedence() { + let include = ["N.T"]; + let exclude = ["N.T"]; + let f = Filter::new(&include, &exclude); + + assert!(!f.is_empty()); + + assert!(!includes_type_name(&f, "N.T")); + } +} diff --git a/crates/libs/metadata/src/reader/mod.rs b/crates/libs/metadata/src/reader/mod.rs index 6240325a44..a82469cada 100644 --- a/crates/libs/metadata/src/reader/mod.rs +++ b/crates/libs/metadata/src/reader/mod.rs @@ -1,6 +1,7 @@ mod blob; mod codes; mod file; +mod filter; mod guid; mod row; mod tree; @@ -11,6 +12,7 @@ pub use super::*; pub use blob::*; pub use codes::*; pub use file::*; +pub use filter::*; pub use guid::*; pub use r#type::*; pub use row::*; @@ -195,6 +197,9 @@ impl<'a> Reader<'a> { for row in 0..file.tables[TABLE_TYPEDEF].len { let key = Row::new(row, TABLE_TYPEDEF, file_index); let namespace = file.str(key.row as _, key.table as _, 2); + if namespace.is_empty() { + continue; + } let name = trim_tick(file.str(key.row as _, key.table as _, 1)); types.entry(namespace).or_default().entry(name).or_default().push(TypeDef(key)); } @@ -208,22 +213,26 @@ impl<'a> Reader<'a> { } Self { files, types, nested } } - pub fn tree(&'a self, root: &'a str, exclude: &[&str]) -> Option { + pub fn tree(&'a self, root: &'a str, filter: &Filter) -> Tree { let mut tree = Tree::from_namespace(""); for ns in self.types.keys() { - if !exclude.iter().any(|x| ns.starts_with(x)) { + if filter.includes_namespace(ns) { tree.insert_namespace(ns, 0); } } - tree.seek(root) + if root.is_empty() { + tree + } else { + tree.seek(root).expect("Namespace not found") + } } // // Hash functions for fast type lookup // - pub fn namespace_types(&self, namespace: &str) -> impl Iterator + '_ { - self.types.get(namespace).map(|types| types.values().flatten().copied()).into_iter().flatten() + pub fn namespace_types(&'a self, namespace: &str, filter: &'a Filter) -> impl Iterator + '_ { + self.types.get(namespace).map(move |types| types.values().flatten().copied().filter(move |ty| filter.includes_type(self, *ty))).into_iter().flatten() } pub fn nested_types(&self, type_def: TypeDef) -> impl Iterator + '_ { self.nested.get(&type_def).map(|map| map.values().copied()).into_iter().flatten() diff --git a/crates/samples/windows/spellchecker/src/main.rs b/crates/samples/windows/spellchecker/src/main.rs index 5551b2dd5e..416c1987fc 100644 --- a/crates/samples/windows/spellchecker/src/main.rs +++ b/crates/samples/windows/spellchecker/src/main.rs @@ -61,7 +61,7 @@ fn main() -> Result<()> { // Get the next suggestion breaking if the call to `Next` failed let mut suggestion = [PWSTR::null()]; unsafe { - let _ = suggestions.Next(&mut suggestion, None); + _ = suggestions.Next(&mut suggestion, None); } if suggestion[0].is_null() { break; diff --git a/crates/tests/implement/tests/data_object.rs b/crates/tests/implement/tests/data_object.rs index e2c4d370cd..5e2d2fbe12 100644 --- a/crates/tests/implement/tests/data_object.rs +++ b/crates/tests/implement/tests/data_object.rs @@ -116,7 +116,7 @@ fn test() -> Result<()> { assert!(!(*i).EnumDAdvise); d.DUnadvise(0)?; - let _ = d.EnumDAdvise(); + _ = d.EnumDAdvise(); assert!((*i).DUnadvise); assert!((*i).EnumDAdvise); diff --git a/crates/tests/interface/tests/non_com_existing.rs b/crates/tests/interface/tests/non_com_existing.rs index 330a9587d6..cd80c804e5 100644 --- a/crates/tests/interface/tests/non_com_existing.rs +++ b/crates/tests/interface/tests/non_com_existing.rs @@ -162,7 +162,7 @@ fn test() -> Result<()> { // Pass the callback to another API (ignore the result)... let mut source = None; - let _ = audio.CreateSourceVoice(&mut source, std::ptr::null(), 0, 0.0, &*callback, None, None); + _ = audio.CreateSourceVoice(&mut source, std::ptr::null(), 0, 0.0, &*callback, None, None); Ok(()) } diff --git a/crates/tests/matrix3x2/tests/matrix3x2.rs b/crates/tests/matrix3x2/tests/matrix3x2.rs index d57f927473..de0c73a5d2 100644 --- a/crates/tests/matrix3x2/tests/matrix3x2.rs +++ b/crates/tests/matrix3x2/tests/matrix3x2.rs @@ -2,5 +2,5 @@ use windows::Foundation::Numerics::Matrix3x2; #[test] fn test() { - let _ = Matrix3x2::rotation(0.0, 1.0, 2.0); + _ = Matrix3x2::rotation(0.0, 1.0, 2.0); } diff --git a/crates/tests/not_dll/tests/win.rs b/crates/tests/not_dll/tests/win.rs index 1a614efbd1..8457140feb 100644 --- a/crates/tests/not_dll/tests/win.rs +++ b/crates/tests/not_dll/tests/win.rs @@ -5,6 +5,6 @@ use windows::Win32::Graphics::Printing::*; #[test] fn test() { unsafe { - let _ = GetSpoolFileHandle(None); + _ = GetSpoolFileHandle(None); } } diff --git a/crates/tests/win32/tests/win32.rs b/crates/tests/win32/tests/win32.rs index 9688ee8518..83a108f7f9 100644 --- a/crates/tests/win32/tests/win32.rs +++ b/crates/tests/win32/tests/win32.rs @@ -51,7 +51,7 @@ fn rect() { #[test] fn dxgi_mode_desc() { - let _ = DXGI_MODE_DESC { + _ = DXGI_MODE_DESC { Width: 1, Height: 2, RefreshRate: DXGI_RATIONAL { Numerator: 3, Denominator: 5 }, diff --git a/crates/tools/gnu/src/main.rs b/crates/tools/gnu/src/main.rs index 3ef97fa999..2c3c730ded 100644 --- a/crates/tools/gnu/src/main.rs +++ b/crates/tools/gnu/src/main.rs @@ -38,7 +38,7 @@ fn build_platform(platform: &str, dlltool: &str, ar: &str) { let libraries = lib::libraries(); let output = std::path::PathBuf::from(format!("crates/targets/{platform}/lib")); - let _ = std::fs::remove_dir_all(&output); + _ = std::fs::remove_dir_all(&output); std::fs::create_dir_all(&output).unwrap(); for (library, functions) in &libraries { diff --git a/crates/tools/lib/src/lib.rs b/crates/tools/lib/src/lib.rs index 5e50636ae0..d7a2ef1fd3 100644 --- a/crates/tools/lib/src/lib.rs +++ b/crates/tools/lib/src/lib.rs @@ -22,7 +22,7 @@ pub fn libraries() -> BTreeMap> { let files = metadata::reader::File::with_default(&[]).unwrap(); let reader = &metadata::reader::Reader::new(&files); let mut libraries = BTreeMap::>::new(); - let root = reader.tree("Windows", &[]).expect("`Windows` namespace not found"); + let root = reader.tree("Windows", &Default::default()); for tree in root.flatten() { if let Some(apis) = reader.get(metadata::reader::TypeName::new(tree.namespace, "Apis")).next() { diff --git a/crates/tools/msvc/src/main.rs b/crates/tools/msvc/src/main.rs index 682a444f34..2771900634 100644 --- a/crates/tools/msvc/src/main.rs +++ b/crates/tools/msvc/src/main.rs @@ -21,7 +21,7 @@ fn main() { let libraries = lib::libraries(); let output = std::path::PathBuf::from("crates/targets/baseline"); - let _ = std::fs::remove_dir_all(&output); + _ = std::fs::remove_dir_all(&output); std::fs::create_dir_all(&output).unwrap(); for (library, functions) in &libraries { diff --git a/crates/tools/sys/src/main.rs b/crates/tools/sys/src/main.rs index a01b25c5f8..ec13772083 100644 --- a/crates/tools/sys/src/main.rs +++ b/crates/tools/sys/src/main.rs @@ -1,8 +1,10 @@ use rayon::prelude::*; -use std::collections::*; use std::io::prelude::*; -/// Namespaces to exclude from code generation for the `windows-sys` crate. +/// Namespaces to include/exclude from code generation for the `windows-sys` crate. + +const INCLUDE_NAMESPACES: [&str; 2] = ["Windows.Win32", "Windows.Wdk"]; + const EXCLUDE_NAMESPACES: [&str; 28] = [ "Windows.Win32.AI.MachineLearning", "Windows.Win32.Graphics.CompositionSwapchain", @@ -35,6 +37,7 @@ const EXCLUDE_NAMESPACES: [&str; 28] = [ ]; fn main() { + let time = std::time::Instant::now(); let mut expect_namespace = false; let mut namespace = String::new(); for arg in std::env::args() { @@ -49,19 +52,17 @@ fn main() { } let mut output = std::path::PathBuf::from("crates/libs/sys/src/Windows"); if namespace.is_empty() { - let _ = std::fs::remove_dir_all(&output); + _ = std::fs::remove_dir_all(&output); } output.pop(); let files = metadata::reader::File::with_default(&[]).unwrap(); let reader = &metadata::reader::Reader::new(&files); if !namespace.is_empty() { - let tree = reader.tree(&namespace, &[]).expect("Namespace not found"); + let tree = reader.tree(&namespace, &Default::default()); gen_tree(reader, &output, &tree); return; } - let win32 = reader.tree("Windows.Win32", &EXCLUDE_NAMESPACES).expect("`Windows.Win32` namespace not found"); - let wdk = reader.tree("Windows.Wdk", &EXCLUDE_NAMESPACES).expect("`Windows.Win32` namespace not found"); - let root = metadata::reader::Tree { namespace: "Windows", nested: BTreeMap::from([("Win32", win32), ("Wdk", wdk)]) }; + let root = reader.tree("Windows", &metadata::reader::Filter::new(&INCLUDE_NAMESPACES, &EXCLUDE_NAMESPACES)); let trees = root.flatten(); trees.par_iter().for_each(|tree| gen_tree(reader, &output, tree)); output.pop(); @@ -109,10 +110,11 @@ default = [] file.write_all(format!("{feature} = []\n").as_bytes()).unwrap(); } } + + println!(" Finished in {:.2}s", time.elapsed().as_secs_f32()); } fn gen_tree(reader: &metadata::reader::Reader, output: &std::path::Path, tree: &metadata::reader::Tree) { - println!("{}", tree.namespace); let mut path = std::path::PathBuf::from(output); path.push(tree.namespace.replace('.', "/")); std::fs::create_dir_all(&path).unwrap(); diff --git a/crates/tools/windows/src/main.rs b/crates/tools/windows/src/main.rs index b0744fdad9..3342ac485a 100644 --- a/crates/tools/windows/src/main.rs +++ b/crates/tools/windows/src/main.rs @@ -5,6 +5,7 @@ use std::io::prelude::*; const EXCLUDE_NAMESPACES: [&str; 14] = ["Windows.AI.MachineLearning.Preview", "Windows.ApplicationModel.SocialInfo", "Windows.Devices.AllJoyn", "Windows.Devices.Perception", "Windows.Security.Authentication.Identity.Provider", "Windows.Services.Cortana", "Windows.System.Power.Diagnostics", "Windows.System.Preview", "Windows.UI.Xaml", "Windows.Win32.Interop", "Windows.Win32.System.Diagnostics.Debug.WebApp", "Windows.Win32.System.WinRT.Xaml", "Windows.Win32.Web.MsHtml", "Windows.Win32.UI.Xaml"]; fn main() { + let time = std::time::Instant::now(); let mut expect_namespace = false; let mut namespace = String::new(); for arg in std::env::args() { @@ -19,17 +20,17 @@ fn main() { } let mut output = std::path::PathBuf::from("crates/libs/windows/src/Windows"); if namespace.is_empty() { - let _ = std::fs::remove_dir_all(&output); + _ = std::fs::remove_dir_all(&output); } output.pop(); let files = metadata::reader::File::with_default(&[]).unwrap(); let reader = &metadata::reader::Reader::new(&files); if !namespace.is_empty() { - let tree = reader.tree(&namespace, &[]).expect("Namespace not found"); + let tree = reader.tree(&namespace, &Default::default()); gen_tree(reader, &output, &tree); return; } - let root = reader.tree("Windows", &EXCLUDE_NAMESPACES).expect("`Windows` namespace not found"); + let root = reader.tree("Windows", &metadata::reader::Filter::new(&["Windows"], &EXCLUDE_NAMESPACES)); let trees = root.flatten(); trees.par_iter().for_each(|tree| gen_tree(reader, &output, tree)); output.pop(); @@ -84,10 +85,11 @@ implement = ["windows-implement", "windows-interface"] file.write_all(format!("{feature} = []\n").as_bytes()).unwrap(); } } + + println!(" Finished in {:.2}s", time.elapsed().as_secs_f32()); } fn gen_tree(reader: &metadata::reader::Reader, output: &std::path::Path, tree: &metadata::reader::Tree) { - println!("{}", tree.namespace); let mut path = std::path::PathBuf::from(output); path.push(tree.namespace.replace('.', "/")); std::fs::create_dir_all(&path).unwrap();