-
Notifications
You must be signed in to change notification settings - Fork 508
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce simpler gen2 crate for code generation (#1369)
- Loading branch information
Showing
19 changed files
with
1,021 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! {} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! {} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! {} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)* | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.