Skip to content

Commit

Permalink
Introduce simpler gen2 crate for code generation (#1369)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr authored Nov 29, 2021
1 parent f302772 commit 65fb01d
Show file tree
Hide file tree
Showing 19 changed files with 1,021 additions and 30 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
if: contains(matrix.other, 'windows-gnu')

- name: Check (${{ matrix.os }})
run: cargo check
run: cargo check --target ${{ matrix.other }}
if: contains(matrix.other, 'windows-gnu')

- name: Test (${{ matrix.os }})
Expand Down
11 changes: 11 additions & 0 deletions crates/deps/gen2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "windows_gen2"
version = "0.28.0"
authors = ["Microsoft"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "Code gen support for the windows crate"

[dependencies]
quote = { package = "windows_quote", path = "../quote", version = "0.28.0" }
reader = { package = "windows_reader", path = "../reader", version = "0.28.0" }
22 changes: 22 additions & 0 deletions crates/deps/gen2/src/callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use super::*;

pub fn gen_callback(def: &TypeDef, gen: &Gen) -> TokenStream {
let name = gen_type_name(def, gen);
let method = def.invoke_method();
let signature = method.signature(&[]);
let return_sig = gen_return_sig(&signature, gen);
let arch_cfg = gen.arch_cfg(def.attributes());
let feature_cfg = gen.method_cfg(&method).0;

let params = signature.params.iter().map(|p| {
let name = gen_param_name(&p.param);
let tokens = gen_abi_param_sig(p, gen);
quote! { #name: #tokens }
});

quote! {
#arch_cfg
#feature_cfg
pub type #name = ::core::option::Option<unsafe extern "system" fn(#(#params),*) #return_sig>;
}
}
19 changes: 19 additions & 0 deletions crates/deps/gen2/src/class.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use super::*;

pub fn gen_class(def: &TypeDef, gen: &Gen) -> TokenStream {
if gen.sys {
let has_default = def.interface_impls().any(|interface| interface.is_default());

if has_default {
let name: TokenStream = def.name().into();

quote! {
pub type #name = *mut ::core::ffi::c_void;
}
} else {
quote! {}
}
} else {
quote! {}
}
}
15 changes: 15 additions & 0 deletions crates/deps/gen2/src/com_interface.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use super::*;

// TODO: add support (even for windows-sys) behind implement feature to implement COM/WinRT interfaces
// TODO: add skeleton support for calling COM/WinRT interfaces for windows-sys and providing their GUIDs
pub fn gen_com_interface(def: &TypeDef, gen: &Gen) -> TokenStream {
if gen.sys {
let name: TokenStream = def.name().into();

quote! {
pub type #name = *mut ::core::ffi::c_void;
}
} else {
quote! {}
}
}
126 changes: 126 additions & 0 deletions crates/deps/gen2/src/constant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#![allow(clippy::many_single_char_names)]

use super::*;

pub fn gen_constant(def: &Field, gen: &Gen) -> TokenStream {
let name = def.name();
let name = gen_ident(name);
let signature = def.signature(None);

let cfg = gen.field_cfg(def);

if let Some(constant) = def.constant() {
if signature.kind == constant.value_type() {
let value = gen_constant_type_value(&constant.value());
quote! {
pub const #name: #value;
}
} else {
let kind = gen_sig(&signature, gen);
let value = gen_constant_value(&constant.value());

let value = if signature.kind.underlying_type() == constant.value_type() {
value
} else {
quote! { #value as _ }
};

if gen.sys && (signature.kind == constant.value_type() || signature.kind.is_handle() || signature.kind == ElementType::HRESULT) {
quote! {
#cfg
pub const #name: #kind = #value;
}
} else {
quote! {
#cfg
pub const #name: #kind = #kind(#value);
}
}
}
} else if let Some(guid) = GUID::from_attributes(def.attributes()) {
let value = gen_guid(&guid, gen);
let guid = gen_element_name(&ElementType::GUID, gen);
quote! { pub const #name: #guid = #value; }
} else if let Some((guid, id)) = get_property_key(def.attributes()) {
let kind = gen_sig(&signature, gen);
let guid = gen_guid(&guid, gen);
quote! {
#cfg
pub const #name: #kind = #kind {
fmtid: #guid,
pid: #id,
};
}
} else {
quote! {}
}
}

pub fn gen_constant_type_value(value: &ConstantValue) -> TokenStream {
match value {
ConstantValue::Bool(value) => quote! { bool = #value },
ConstantValue::U8(value) => quote! { u8 = #value },
ConstantValue::I8(value) => quote! { i8 = #value },
ConstantValue::U16(value) => quote! { u16 = #value },
ConstantValue::I16(value) => quote! { i16 = #value },
ConstantValue::U32(value) => quote! { u32 = #value },
ConstantValue::I32(value) => quote! { i32 = #value },
ConstantValue::U64(value) => quote! { u64 = #value },
ConstantValue::I64(value) => quote! { i64 = #value },
ConstantValue::F32(value) => quote! { f32 = #value },
ConstantValue::F64(value) => quote! { f64 = #value },
ConstantValue::String(value) => quote! { &'static str = #value },
_ => unimplemented!(),
}
}

pub fn gen_guid(value: &GUID, gen: &Gen) -> TokenStream {
let guid = gen_element_name(&ElementType::GUID, gen);

if gen.sys {
let a = Literal::u32_unsuffixed(value.0);
let b = Literal::u16_unsuffixed(value.1);
let c = Literal::u16_unsuffixed(value.2);
let d = Literal::u8_unsuffixed(value.3);
let e = Literal::u8_unsuffixed(value.4);
let f = Literal::u8_unsuffixed(value.5);
let g = Literal::u8_unsuffixed(value.6);
let h = Literal::u8_unsuffixed(value.7);
let i = Literal::u8_unsuffixed(value.8);
let j = Literal::u8_unsuffixed(value.9);
let k = Literal::u8_unsuffixed(value.10);

// TODO: once code complete measure how much longer it takes if-any to use from_u128 to produce a more compact package

quote! {
#guid { data1:#a, data2:#b, data3:#c, data4:[#d, #e, #f, #g, #h, #i, #j, #k] }
}
} else {
format!("{}::from_u128(0x{:08x?}_{:04x?}_{:04x?}_{:02x?}{:02x?}_{:02x?}{:02x?}{:02x?}{:02x?}{:02x?}{:02x?})", guid.into_string(), value.0, value.1, value.2, value.3, value.4, value.5, value.6, value.7, value.8, value.9, value.10).into()
}
}

pub fn gen_constant_value(value: &ConstantValue) -> TokenStream {
match value {
ConstantValue::Bool(value) => quote! { #value },
ConstantValue::U8(value) => quote! { #value },
ConstantValue::I8(value) => quote! { #value },
ConstantValue::U16(value) => quote! { #value },
ConstantValue::I16(value) => quote! { #value },
ConstantValue::U32(value) => quote! { #value },
ConstantValue::I32(value) => quote! { #value },
ConstantValue::U64(value) => quote! { #value },
ConstantValue::I64(value) => quote! { #value },
ConstantValue::F32(value) => quote! { #value },
ConstantValue::F64(value) => quote! { #value },
ConstantValue::String(value) => quote! { #value },
_ => unimplemented!(),
}
}

fn get_property_key(attributes: impl Iterator<Item = Attribute>) -> Option<(GUID, u32)> {
attributes.into_iter().find(|attribute| attribute.name() == "PropertyKeyAttribute").map(|attribute| {
let args = attribute.args();
(GUID::from_args(&args), args[11].1.unwrap_u32())
})
}
13 changes: 13 additions & 0 deletions crates/deps/gen2/src/delegate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::*;

pub fn gen_delegate(def: &TypeDef, gen: &Gen) -> TokenStream {
if gen.sys {
let name = gen_generic_ident(def.name());

quote! {
pub type #name = *mut ::core::ffi::c_void;
}
} else {
quote! {}
}
}
61 changes: 61 additions & 0 deletions crates/deps/gen2/src/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use super::*;

pub fn gen_enum(def: &TypeDef, gen: &Gen) -> TokenStream {
// TODO: use same representation for unscoped enums
if gen.sys {
gen_sys_enum(def, gen)
} else {
quote! {}
}
}

fn gen_sys_enum(def: &TypeDef, gen: &Gen) -> TokenStream {
let name = gen_ident(def.name());
let underlying_type = def.underlying_type();
let underlying_type = gen_element_name(&underlying_type, gen);

let fields = def.fields().filter_map(|field| {
if field.is_literal() {
let field_name = gen_ident(field.name());
let constant = field.constant().unwrap();
let value = gen_constant_value(&constant.value());

Some((field_name, value))
} else {
None
}
});

if def.is_scoped() {
let fields = fields.map(|(field_name, value)| {
quote! {
pub const #field_name: Self = Self(#value);
}
});

quote! {
#[repr(transparent)]
pub struct #name(pub #underlying_type);
impl #name {
#(#fields)*
}
impl ::core::marker::Copy for #name {}
impl ::core::clone::Clone for #name {
fn clone(&self) -> Self {
*self
}
}
}
} else {
let fields = fields.map(|(field_name, value)| {
quote! {
pub const #field_name: #name = #value;
}
});

quote! {
pub type #name = #underlying_type;
#(#fields)*
}
}
}
63 changes: 63 additions & 0 deletions crates/deps/gen2/src/function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use super::*;

pub fn gen_functions(tree: &TypeTree, gen: &Gen) -> TokenStream {
let mut functions = tree.types.values().map(|entry| gen_function_if(entry, gen)).peekable();

if functions.peek().is_some() {
quote! {
#[link(name = "windows")]
extern "system" {
#(#functions)*
}
}
} else {
quote! {}
}
}

pub fn gen_function(def: &MethodDef, gen: &Gen) -> TokenStream {
if gen.sys {
let function = gen_function_decl(def, gen);

quote! {
#[link(name = "windows")]
extern "system" {
#function
}
}
} else {
quote! {}
}
}

fn gen_function_if(entry: &TypeEntry, gen: &Gen) -> TokenStream {
let mut tokens = TokenStream::new();

for def in &entry.def {
if let ElementType::MethodDef(def) = def {
tokens.combine(&gen_function_decl(def, gen));
}
}

tokens
}

fn gen_function_decl(def: &MethodDef, gen: &Gen) -> TokenStream {
let name = gen_ident(def.name());
let signature = def.signature(&[]);
let return_type = gen_return_sig(&signature, gen);
let arch_cfg = gen.arch_cfg(def.attributes());
let feature_cfg = gen.method_cfg(def).0;

let params = signature.params.iter().map(|p| {
let name = gen_param_name(&p.param);
let tokens = gen_param_sig(p, gen);
quote! { #name: #tokens }
});

quote! {
#arch_cfg
#feature_cfg
pub fn #name(#(#params),*) #return_type;
}
}
Loading

0 comments on commit 65fb01d

Please sign in to comment.