Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/libs/bindgen/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct Config<'a> {
pub sys_fn_ptrs: bool,
pub sys_fn_extern: bool,
pub implement: bool,
pub implements: &'a Implements,
pub noexcept: bool,
pub specific_deps: bool,
pub derive: &'a Derive,
Expand All @@ -39,6 +40,23 @@ impl Config<'_> {
clone.namespace = namespace;
clone
}

/// Returns `true` if the `_Impl` scaffolding for the given type should be
/// emitted, based on the `--implement` and `--implements` options.
///
/// `default` is the behavior to fall back on when neither option restricts
/// emission: `true` for types that are emitted unconditionally today (such
/// as COM/Win32 interfaces) and `false` (or some other condition such as
/// `!is_exclusive`) for WinRT interfaces.
pub fn should_implement(&self, name: TypeName, default: bool) -> bool {
if self.implement {
true
} else if !self.implements.is_empty() {
self.implements.matches(name)
} else {
default
}
}
}

impl<'a> Config<'a> {
Expand Down
46 changes: 46 additions & 0 deletions crates/libs/bindgen/src/implements.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use super::*;

/// Matcher used by the `--implements` option to determine whether the `_Impl`
/// scaffolding for a given type should be emitted.
///
/// Patterns may be a fully-qualified type name (`Namespace.Name`) or a
/// namespace prefix (which matches every type under that namespace).
#[derive(Debug, Default)]
pub struct Implements(Vec<String>);

impl Implements {
pub fn new(patterns: &[&str]) -> Self {
Self(patterns.iter().map(|s| s.to_string()).collect())
}

pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn matches(&self, name: TypeName) -> bool {
self.0
.iter()
.any(|rule| match_type_name(rule, name.namespace(), name.name()))
}
}

/// Returns `true` if `rule` selects the type `namespace.name`. A `rule` whose
/// length is less than or equal to `namespace.len()` is treated as a namespace
/// prefix and matches every type whose namespace starts with `rule`. Otherwise
/// `rule` must be a fully-qualified `Namespace.Name` whose namespace component
/// equals `namespace` exactly and whose name component equals `name` exactly.
fn match_type_name(rule: &str, namespace: &str, name: &str) -> bool {
if rule.len() <= namespace.len() {
return namespace_starts_with(namespace, rule);
}

if !rule.starts_with(namespace) {
return false;
}

if rule.as_bytes()[namespace.len()] != b'.' {
return false;
}

name == &rule[namespace.len() + 1..]
}
34 changes: 34 additions & 0 deletions crates/libs/bindgen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod derive;
mod derive_writer;
mod filter;
mod guid;
mod implements;
mod index;
mod io;
mod libraries;
Expand All @@ -31,6 +32,7 @@ use derive::*;
use derive_writer::*;
use filter::*;
use guid::*;
use implements::*;
use io::*;
pub use libraries::*;
use param::*;
Expand Down Expand Up @@ -86,6 +88,7 @@ pub fn builder() -> Bindgen {
/// | `--sys-fn-ptrs` | Additionally generates function pointers for sys-style Rust bindings. |
/// | `--sys-fn-extern` | Generates extern declarations rather than link macros for sys-style Rust bindings. |
/// | `--implement` | Includes implementation traits for WinRT interfaces. |
/// | `--implements` | Includes implementation traits for the listed types only. |
/// | `--noexcept` | Assumes all WinRT methods do not raise exceptions. |
/// | `--link` | Overrides the default `windows-link` implementation for system calls. |
///
Expand Down Expand Up @@ -363,6 +366,7 @@ where
"--implement" => {
builder.implement();
}
"--implements" => kind = ArgKind::Implements,
"--noexcept" => {
builder.noexcept();
}
Expand Down Expand Up @@ -394,6 +398,9 @@ where
ArgKind::Derive => {
builder.derive(arg);
}
ArgKind::Implements => {
builder.implements([arg]);
}
ArgKind::Rustfmt => {
builder.rustfmt(arg);
}
Expand Down Expand Up @@ -434,6 +441,7 @@ pub struct Bindgen {
output: String,
references: Vec<String>,
derive: Vec<String>,
implements: Vec<String>,
rustfmt: String,
link: String,
flat: bool,
Expand Down Expand Up @@ -597,6 +605,28 @@ impl Bindgen {
self
}

/// Add types to the list of types for which implementation scaffolding
/// should be emitted.
///
/// Each entry may be a fully-qualified type name (`Namespace.Name`) or a
/// namespace prefix that matches every type defined under it. When at
/// least one entry is provided and `--implement` is **not** set, the
/// `_Impl` scaffolding is generated only for types matching the list,
/// rather than for every interface/class in the filter set. This is a
/// finer-grained alternative to the broad `--implement` switch and can
/// significantly reduce build time when only a handful of interfaces
/// need to be implemented.
pub fn implements<I, S>(&mut self, names: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
for name in names {
self.implements.push(name.as_ref().to_string());
}
self
}

/// Assume that all WinRT methods do not raise exceptions, regardless of whether
/// they have the `NoExceptionAttribute`. This causes bindgen to emit infallible
/// signatures for HRESULT-returning WinRT methods, skipping `Result` propagation.
Expand Down Expand Up @@ -765,11 +795,13 @@ impl Bindgen {
}

let derive_str: Vec<&str> = self.derive.iter().map(|s| s.as_str()).collect();
let implements_str: Vec<&str> = self.implements.iter().map(|s| s.as_str()).collect();

let filter = Filter::new(&reader, &include, &exclude);
let references = References::new(&reader, references);
let types = TypeMap::filter(&reader, &filter, &references);
let derive = Derive::new(&reader, &types, &derive_str);
let implements = Implements::new(&implements_str);
let warnings = WarningBuilder::default();

let config = Config {
Expand All @@ -790,6 +822,7 @@ impl Bindgen {
sys_fn_ptrs: self.sys_fn_ptrs,
sys_fn_extern: self.sys_fn_extern,
implement: self.implement,
implements: &implements,
noexcept: self.noexcept,
specific_deps: self.specific_deps,
link,
Expand All @@ -816,6 +849,7 @@ enum ArgKind {
Rustfmt,
Reference,
Derive,
Implements,
Link,
}

Expand Down
158 changes: 82 additions & 76 deletions crates/libs/bindgen/src/types/cpp_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,54 +234,59 @@ impl CppInterface {
});
}

let impl_name: TokenStream = format!("{}_Impl", self.def.name()).into();

let cfg = if config.package {
fn combine(interface: &CppInterface, dependencies: &mut TypeMap, config: &Config) {
for method in interface.get_methods(config).iter() {
if let CppMethodOrName::Method(method) = method {
dependencies.combine(&method.dependencies);
if config.should_implement(self.def.type_name(), true) {
let impl_name: TokenStream = format!("{}_Impl", self.def.name()).into();

let cfg = if config.package {
fn combine(
interface: &CppInterface,
dependencies: &mut TypeMap,
config: &Config,
) {
for method in interface.get_methods(config).iter() {
if let CppMethodOrName::Method(method) = method {
dependencies.combine(&method.dependencies);
}
}
}
}

let mut dependencies = self.dependencies(config.reader);
combine(self, &mut dependencies, config);

base_interfaces.iter().for_each(|interface| {
if let Type::CppInterface(ty) = interface {
combine(ty, &mut dependencies, config);
}
});

Cfg::new(&dependencies, config).write(config, false)
} else {
quote! {}
};
let mut dependencies = self.dependencies(config.reader);
combine(self, &mut dependencies, config);

let mut names = MethodNames::new();
base_interfaces.iter().for_each(|interface| {
if let Type::CppInterface(ty) = interface {
combine(ty, &mut dependencies, config);
}
});

let field_methods: Vec<_> = methods
.iter()
.map(|method| match method {
CppMethodOrName::Method(method) => {
let name = names.add(method.def);
if has_unknown_base {
quote! { #name: #name::<Identity, OFFSET>, }
} else {
quote! { #name: #name::<Identity>, }
Cfg::new(&dependencies, config).write(config, false)
} else {
quote! {}
};

let mut names = MethodNames::new();

let field_methods: Vec<_> = methods
.iter()
.map(|method| match method {
CppMethodOrName::Method(method) => {
let name = names.add(method.def);
if has_unknown_base {
quote! { #name: #name::<Identity, OFFSET>, }
} else {
quote! { #name: #name::<Identity>, }
}
}
}
CppMethodOrName::Name(method) => {
let name = names.add(*method);
quote! { #name: 0, }
}
})
.collect();
CppMethodOrName::Name(method) => {
let name = names.add(*method);
quote! { #name: 0, }
}
})
.collect();

let mut names = MethodNames::new();
let mut names = MethodNames::new();

let impl_methods: Vec<_> = methods.iter().map(|method| match method {
let impl_methods: Vec<_> = methods.iter().map(|method| match method {
CppMethodOrName::Method(method) => {
let name = names.add(method.def);
let signature = method.write_abi(config, true);
Expand Down Expand Up @@ -311,23 +316,23 @@ impl CppInterface {
_ => quote! {},
}).collect();

let mut names = MethodNames::new();
let mut names = MethodNames::new();

let trait_methods: Vec<_> = methods
.iter()
.map(|method| match method {
CppMethodOrName::Method(method) => {
let name = names.add(method.def);
let signature = method.write_impl_signature(config, true);
quote! { fn #name #signature; }
}
_ => quote! {},
})
.collect();
let trait_methods: Vec<_> = methods
.iter()
.map(|method| match method {
CppMethodOrName::Method(method) => {
let name = names.add(method.def);
let signature = method.write_impl_signature(config, true);
quote! { fn #name #signature; }
}
_ => quote! {},
})
.collect();

let impl_base = base_interfaces.last().map(|ty| ty.write_impl_name(config));
let impl_base = base_interfaces.last().map(|ty| ty.write_impl_name(config));

let field_base = base_interfaces.last().map(|ty|{
let field_base = base_interfaces.last().map(|ty|{
match ty {
Type::IUnknown => quote! { base__: windows_core::IUnknown_Vtbl::new::<Identity, OFFSET>(), },
Type::Object => quote! { base__: windows_core::IInspectable_Vtbl::new::<Identity, #name, OFFSET>(), },
Expand All @@ -343,30 +348,30 @@ impl CppInterface {
}
});

// If any methods were skipped due to missing dependencies, the interface cannot be
// fully described, so omit the ability to implement it rather than emitting a
// partial vtable with null function pointer slots. Also propagate the omission
// when any base interface had its `_Impl` trait omitted, since a derived `_Impl`
// cannot reference a base `_Impl` that wasn't emitted.
let has_skipped_methods = methods
.iter()
.any(|method| matches!(method, CppMethodOrName::Name(_)))
|| base_interfaces.iter().any(|ty| match ty {
Type::CppInterface(ty) => ty.has_skipped_methods(config),
_ => false,
});
// If any methods were skipped due to missing dependencies, the interface cannot be
// fully described, so omit the ability to implement it rather than emitting a
// partial vtable with null function pointer slots. Also propagate the omission
// when any base interface had its `_Impl` trait omitted, since a derived `_Impl`
// cannot reference a base `_Impl` that wasn't emitted.
let has_skipped_methods = methods
.iter()
.any(|method| matches!(method, CppMethodOrName::Name(_)))
|| base_interfaces.iter().any(|ty| match ty {
Type::CppInterface(ty) => ty.has_skipped_methods(config),
_ => false,
});

if has_skipped_methods {
config.warnings.skip_implement(self.def);
if has_skipped_methods {
config.warnings.skip_implement(self.def);

if has_unknown_base {
result.combine(quote! {
#cfg
impl windows_core::RuntimeName for #name {}
});
}
} else {
result.combine( if has_unknown_base {
if has_unknown_base {
result.combine(quote! {
#cfg
impl windows_core::RuntimeName for #name {}
});
}
} else {
result.combine( if has_unknown_base {
let matches = base_interfaces.iter().filter_map(|ty|{
match ty {
Type::CppInterface(ty) => {
Expand Down Expand Up @@ -432,6 +437,7 @@ impl CppInterface {
}
}
});
}
}

result
Expand Down
2 changes: 1 addition & 1 deletion crates/libs/bindgen/src/types/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ impl Interface {
}
}

if config.implement || !is_exclusive {
if config.should_implement(type_name, !is_exclusive) {
let impl_name: TokenStream = format!("{}_Impl", trim_tick(self.def.name())).into();

let generics: Vec<_> = self
Expand Down
Loading
Loading