Skip to content

Instantly share code, notes, and snippets.

@kennykerr
Created May 12, 2026 18:02
Show Gist options
  • Select an option

  • Save kennykerr/2c2fc871a0b28d1acc1c0bb43d6b78e9 to your computer and use it in GitHub Desktop.

Select an option

Save kennykerr/2c2fc871a0b28d1acc1c0bb43d6b78e9 to your computer and use it in GitHub Desktop.

Upstream proposal: method-level entries in --filter

Design note to file against microsoft/windows-rs. No code changes in this repo yet — the work depends on bindgen-side support landing first.

Motivation

Type-level --filter in crates/tools/bindings/src/main.rs is at its empirical minimum (see the bindings filter memory: every entry is reachable from code we actually use). Despite that, crates/libs/reactor/src/bindings.rs still ships ~32k LoC and ~1,560 pub fns, the bulk of which are unused. Worst offenders today:

Interface Methods generated Methods we call
IUIElement 116 ~5
IFrameworkElement ~100 ~30
IControl ~70 ~20
IAutomationPeerOverrides 38 0

The next lever is method granularity.

Key insight

windows-bindgen --minimal already demotes vtable slots to anonymous Foo: usize whenever a method's signature references a type outside --filter, preserving COM offsets and skipping the matching impl wrapper. The emit-as-opaque-slot mechanism exists; only a by-name trigger is missing.

Proposal

Extend the existing --filter to accept method-level entries — no new options, no config file, no new reporter. Existing callers are unaffected because the new entries only appear if you write a :: in them.

Entry grammar (extension of today's Ns.Type form)

Ns.Type                       # keep type, all methods (today's behavior)
Ns.Type::Method               # keep this method; demote unlisted methods on Type
Ns.Type::Property             # sugar for get_Property + put_Property
Ns.Type::Event                # sugar for add_Event + remove_Event
!Ns.Type::Method              # exclude (demote) this method; keep rest
!Ns.Type                      # exclude type (today's behavior — already supported via `!`)

The ! exclude prefix is reused from the existing exclude mechanism; the only new thing is ::Method after a type name.

Semantics

  • Plain Ns.Type keeps current "all methods kept" behavior — fully backwards-compatible.
  • The first Ns.Type::… entry for a given type flips that type into allowlist mode: only listed methods are kept, the rest demote to Slot: usize. Multiple Ns.Type::Method entries accumulate.
  • !Ns.Type::Method is the denylist form: type stays in keep-all mode, only the listed methods demote. Mixing allow + deny entries on the same type is an error.
  • Property/event sugar expands at parse time.

Invariants

  • Vtable layout sacred. Demoted methods become Slot: usize at their exact original offset — same mechanism --minimal already uses for signature-pruned methods. ABI-safe by construction.
  • Method filter runs after type filter. Cannot remove types; only methods on already-kept types. Result types referenced only by demoted methods stay kept (no transitive recompute).
  • Inheritance opaque. A filter on a derived interface only affects its own slots; base interfaces filter independently.
  • --implements overrides. Methods required by an implemented interface (e.g. our IApplicationOverrides, IXamlMetadataProvider) are always emitted; explicit exclusion is a hard error.
  • Unused entries already covered. Filter entries that match nothing show up in the existing bindgen Warnings output — no new reporter needed.

Implementation sketch (bindgen side)

The per-method decision point in the writer already chooses between typed slot and usize. Extend the --filter parser to split on ::, record an optional MethodFilter { Keep(set) | Exclude(set) } per type. In the writer, after the existing signature-reachability check, consult the filter and demote rejected methods. Expand property/event sugar at the same place the writer already collapses get_X / put_X into one pub fn. Skip the matching impl wrapper (same gate). Short-circuit for --implements methods. No winmd-schema changes, no offset analysis, no new CLI args.

Expected local impact

Modest denylists across the heavy interfaces project to ~15–25% trim of bindings.rs at zero ABI risk. The win compounds because pruned methods drop their impl wrappers, agile-call shims, and any reachable-but-otherwise- unused parameter types.

Risks / non-goals

  • Not a security boundary. Demoted offsets are derivable; raw-vtable calls still work. This is a binary-size / compile-time tool, not an API gate — document accordingly.
  • Diff churn. Toggling method-filter membership shifts no offsets (slot stays : usize), so iteration is safe.
  • Out of scope: filtering by winmd attribute ([Deprecated], contract version), struct fields, enum variants.

Suggested rollout

  1. !Ns.Type::Method (denylist) — lower authoring risk; touch only the heaviest interfaces.
  2. Ns.Type::Method (allowlist) — for surgical surfaces like IAutomationPeerOverrides where almost nothing is used.
  3. Property/event sugar.

Follow-up here once landed

Add !Ns.Type::Method lines to the existing --filter array in crates/tools/bindings/src/main.rs per heavy interface, each block independently verifiable via cargo check --target x86_64-pc-windows-msvc -p windows-reactor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment