Metadata generation of attributes and constants (#2844)

This commit is contained in:
Kenny Kerr 2024-02-13 10:17:27 -06:00 коммит произвёл GitHub
Родитель 7ce7894932
Коммит ece23903d1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 203 добавлений и 115 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -1,6 +1,5 @@
/.vscode
/.vs
/target
/temp
*.lock
*.winmd

Просмотреть файл

@ -70,7 +70,7 @@ fn write_interface(writer: &mut winmd::Writer, namespace: &str, name: &str, memb
}
writer.tables.TypeDef.push(winmd::TypeDef {
Extends: 0,
Extends: winmd::TypeDefOrRef::none(),
FieldList: writer.tables.Field.len() as u32,
MethodList: writer.tables.MethodDef.len() as u32,
Flags: flags.0,
@ -82,7 +82,7 @@ fn write_interface(writer: &mut winmd::Writer, namespace: &str, name: &str, memb
writer.tables.GenericParam.push(writer::GenericParam {
Number: number as u16,
Flags: 0,
Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1).encode(),
Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1),
Name: writer.strings.insert(generic),
});
}

Просмотреть файл

@ -1,4 +1,5 @@
use super::*;
use metadata::{AsRow, HasAttributes};
pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap<&str, &str>, output: &str) -> Result<()> {
let mut writer = Writer::new(output);
@ -22,7 +23,7 @@ pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap
let generics = &metadata::type_def_generics(def);
let extends = if let Some(extends) = def.extends() { writer.insert_type_ref(extends.namespace, extends.name) } else { 0 };
let extends = if let Some(extends) = def.extends() { writer.insert_type_ref(extends.namespace, extends.name) } else { TypeDefOrRef::none() };
writer.tables.TypeDef.push(TypeDef {
Extends: extends,
@ -33,11 +34,13 @@ pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap
TypeNamespace: writer.strings.insert(def.namespace()),
});
let def_ref = writer.tables.TypeDef.len() as u32 - 1;
for generic in def.generics() {
writer.tables.GenericParam.push(GenericParam {
Number: generic.number(), // TODO: isn't this just going to be incremental?
Flags: 0,
Owner: TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1).encode(),
Owner: TypeOrMethodDef::TypeDef(def_ref),
Name: writer.strings.insert(generic.name()),
});
}
@ -53,7 +56,7 @@ pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap
rest => unimplemented!("{rest:?}"),
};
writer.tables.InterfaceImpl.push(InterfaceImpl { Class: writer.tables.TypeDef.len() as u32 - 1, Interface: reference });
writer.tables.InterfaceImpl.push(InterfaceImpl { Class: def_ref, Interface: reference });
}
// TODO: if the class is "Apis" then should we sort the fields (constants) and methods (functions) for stability
@ -63,6 +66,10 @@ pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap
let signature = writer.insert_field_sig(&ty);
writer.tables.Field.push(Field { Flags: field.flags().0, Name: writer.strings.insert(field.name()), Signature: signature });
if let Some(constant) = field.constant() {
writer.tables.Constant.push(Constant { Type: constant.usize(0) as u16, Parent: HasConstant::Field(writer.tables.Field.len() as u32 - 1), Value: writer.blobs.insert(&constant.blob(2)) })
}
}
for method in def.methods() {
@ -85,6 +92,44 @@ pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap
writer.tables.Param.push(Param { Flags: param.flags().0, Sequence: param.sequence(), Name: writer.strings.insert(param.name()) });
}
}
for attribute in def.attributes() {
let metadata::AttributeType::MemberRef(attribute_ctor) = attribute.ty();
assert_eq!(attribute_ctor.name(), ".ctor");
let metadata::MemberRefParent::TypeRef(attribute_type) = attribute_ctor.parent();
let attribute_type_ref = if let TypeDefOrRef::TypeRef(type_ref) = writer.insert_type_ref(attribute_type.namespace(), attribute_type.name()) { MemberRefParent::TypeRef(type_ref) } else { panic!() };
let signature = attribute_ctor.signature();
let return_type = winmd_type(&signature.return_type);
let param_types: Vec<Type> = signature.params.iter().map(winmd_type).collect();
let signature = writer.insert_method_sig(signature.call_flags, &return_type, &param_types);
writer.tables.MemberRef.push(MemberRef { Class: attribute_type_ref, Name: writer.strings.insert(".ctor"), Signature: signature });
let mut values = 1u16.to_le_bytes().to_vec(); // prolog
let args = attribute.args();
let mut named_arg_count = false;
for (index, (name, value)) in args.iter().enumerate() {
value_blob(value, &mut values);
if !named_arg_count && !name.is_empty() {
named_arg_count = true;
let named_arg_count = (args.len() - index) as u16;
values.extend_from_slice(&named_arg_count.to_le_bytes());
break;
}
}
if !named_arg_count {
values.extend_from_slice(&0u16.to_le_bytes());
}
let values = writer.blobs.insert(&values);
writer.tables.CustomAttribute.push(CustomAttribute { Parent: HasAttribute::TypeDef(def_ref), Type: AttributeType::MemberRef(writer.tables.MemberRef.len() as u32 - 1), Value: values });
}
}
// TODO: In theory, `config` could instruct this function to balance the types across a number of winmd files
@ -92,6 +137,35 @@ pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap
write_to_file(output, writer.into_stream()).map_err(|err| err.with_path(output))
}
// TODO: need a Blob type for writing
fn value_blob(value: &metadata::Value, blob: &mut Vec<u8>) {
match value {
metadata::Value::Bool(value) => {
if *value {
blob.push(1)
} else {
blob.push(0)
}
}
metadata::Value::U32(value) => blob.extend_from_slice(&value.to_le_bytes()),
metadata::Value::I32(value) => blob.extend_from_slice(&value.to_le_bytes()),
metadata::Value::U16(value) => blob.extend_from_slice(&value.to_le_bytes()),
metadata::Value::U8(value) => blob.extend_from_slice(&value.to_le_bytes()),
metadata::Value::EnumDef(_def, value) => value_blob(value, blob),
metadata::Value::TypeName(value) => {
let value = value.to_string();
usize_blob(value.len(), blob);
blob.extend_from_slice(value.as_bytes());
}
metadata::Value::String(value) => {
usize_blob(value.len(), blob);
blob.extend_from_slice(value.as_bytes());
}
rest => unimplemented!("{rest:?}"),
}
}
// TODO: keep the basic type conversion
fn winmd_type(ty: &metadata::Type) -> Type {
match ty {

Просмотреть файл

@ -1,71 +1,69 @@
#![allow(dead_code, clippy::enum_variant_names)]
/// A `ResolutionScope` is an index into a certain table indicating the scope in which a TypeRef can be resolved.
#[derive(Clone)]
pub enum ResolutionScope {
Module(u32),
ModuleRef(u32),
AssemblyRef(u32),
TypeRef(u32),
}
impl ResolutionScope {
pub fn encode(&self) -> u32 {
match self {
Self::Module(row) => (row + 1) << 2,
Self::ModuleRef(row) => ((row + 1) << 2) + 1,
Self::AssemblyRef(row) => ((row + 1) << 2) + 2,
Self::TypeRef(row) => ((row + 1) << 2) + 3,
macro_rules! code {
($name:ident($size:literal) $(($table:ident, $code:literal))+) => {
#[derive(Clone, Copy)]
pub enum $name {
$($table(u32),)*
}
}
impl $name {
pub fn encode(&self) -> u32 {
match self {
$(Self::$table(row) => (row.overflowing_add(1).0) << $size | $code,)*
}
}
}
};
}
/// A `TypeDefOrRef` is an index into a certain table used to locate a type definition.
#[derive(Clone)]
pub enum TypeDefOrRef {
TypeDef(u32),
TypeRef(u32),
TypeSpec(u32),
code! { AttributeType(3)
(MemberRef, 3)
}
// TODO: needs to be called HasCustomAttribute
code! { HasAttribute(5)
(MethodDef, 0)
(Field, 1)
(TypeRef, 2)
(TypeDef, 3)
(Param, 4)
(InterfaceImpl, 5)
(MemberRef, 6)
(TypeSpec, 13)
(GenericParam, 19)
}
code! { HasConstant(2)
(Field, 0)
}
code! { MemberForwarded(1)
(MethodDef, 1)
}
code! { MemberRefParent(3)
(TypeRef, 1)
}
code! { TypeDefOrRef(2)
(TypeDef, 0)
(TypeRef, 1)
(TypeSpec, 2)
}
impl TypeDefOrRef {
pub fn encode(&self) -> u32 {
match self {
Self::TypeDef(row) => (row + 1) << 2,
Self::TypeRef(row) => ((row + 1) << 2) + 1,
Self::TypeSpec(row) => ((row + 1) << 2) + 2,
}
pub fn none() -> Self {
TypeDefOrRef::TypeDef(u32::MAX)
}
}
/// A `HasConstant` is an index into a certain table used to identify the parent of a row in the `Constant` table.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum HasConstant {
Field(u32),
Param(u32),
Property(u32),
code! { TypeOrMethodDef(1)
(TypeDef, 0)
}
impl HasConstant {
pub fn encode(&self) -> u32 {
match self {
Self::Field(row) => (row + 1) << 2,
Self::Param(row) => ((row + 1) << 2) + 1,
Self::Property(row) => ((row + 1) << 2) + 2,
}
}
}
/// A `TypeOrMethodDef` is an index into a certain table used to locate the owner of a generic parameter.
#[derive(Clone)]
pub enum TypeOrMethodDef {
TypeDef(u32),
}
impl TypeOrMethodDef {
pub fn encode(&self) -> u32 {
match self {
Self::TypeDef(row) => (row + 1) << 1,
}
}
code! { ResolutionScope(2)
(Module, 0)
(ModuleRef, 1)
(AssemblyRef, 2)
(TypeRef, 3)
}

Просмотреть файл

@ -19,10 +19,10 @@ pub struct Writer {
pub blobs: Blobs,
pub strings: Strings,
pub tables: Tables,
pub scopes: HashMap<String, u32>,
pub scopes: HashMap<String, ResolutionScope>,
// TODO: is this faster than jsut using a single HashMap with a (String,String) key?
pub type_refs: HashMap<String, HashMap<String, u32>>,
pub type_specs: HashMap<Type, u32>,
pub type_refs: HashMap<String, HashMap<String, TypeDefOrRef>>,
pub type_specs: HashMap<Type, TypeDefOrRef>,
}
impl Writer {
@ -36,7 +36,7 @@ impl Writer {
type_specs: Default::default(),
};
writer.tables.TypeDef.push(TypeDef { TypeName: writer.strings.insert("<Module>"), ..Default::default() });
writer.tables.TypeDef.push(TypeDef { TypeName: writer.strings.insert("<Module>"), Flags: 0, TypeNamespace: 0, Extends: TypeDefOrRef::none(), FieldList: 0, MethodList: 0 });
let name = name.rsplit_once(&['/', '\\']).map_or(name, |(_, name)| name);
@ -88,7 +88,7 @@ impl Writer {
self.blobs.insert(&blob)
}
fn insert_scope(&mut self, namespace: &str) -> u32 {
fn insert_scope(&mut self, namespace: &str) -> ResolutionScope {
if let Some(scope) = self.scopes.get(namespace) {
*scope
} else if namespace == "System" {
@ -97,8 +97,7 @@ impl Writer {
MajorVersion: 4,
PublicKeyOrToken: self.blobs.insert(&[0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89]), // TODO: comment on this
..Default::default()
}))
.encode();
}));
self.scopes.insert(namespace.to_string(), scope);
scope
} else {
@ -111,14 +110,13 @@ impl Writer {
RevisionNumber: 0xFF,
Flags: metadata::AssemblyFlags::WindowsRuntime.0,
..Default::default()
}))
.encode();
}));
self.scopes.insert(namespace.to_string(), scope);
scope
}
}
pub fn insert_type_ref(&mut self, namespace: &str, name: &str) -> u32 {
pub fn insert_type_ref(&mut self, namespace: &str, name: &str) -> TypeDefOrRef {
if let Some(key) = self.type_refs.get(namespace) {
if let Some(reference) = key.get(name) {
return *reference;
@ -127,12 +125,12 @@ impl Writer {
let scope = self.insert_scope(namespace);
let reference = TypeDefOrRef::TypeRef(self.tables.TypeRef.push2(TypeRef { TypeName: self.strings.insert(name), TypeNamespace: self.strings.insert(namespace), ResolutionScope: scope })).encode();
let reference = TypeDefOrRef::TypeRef(self.tables.TypeRef.push2(TypeRef { TypeName: self.strings.insert(name), TypeNamespace: self.strings.insert(namespace), ResolutionScope: scope }));
self.type_refs.entry(namespace.to_string()).or_default().insert(name.to_string(), reference);
reference
}
pub fn insert_type_spec(&mut self, ty: Type) -> u32 {
pub fn insert_type_spec(&mut self, ty: Type) -> TypeDefOrRef {
if let Some(key) = self.type_specs.get(&ty) {
return *key;
}
@ -141,8 +139,7 @@ impl Writer {
self.type_blob(&ty, &mut blob);
let signature = self.blobs.insert(&blob);
let reference = TypeDefOrRef::TypeSpec(self.tables.TypeSpec.push2(TypeSpec { Signature: signature })).encode();
let reference = TypeDefOrRef::TypeSpec(self.tables.TypeSpec.push2(TypeSpec { Signature: signature }));
self.type_specs.insert(ty, reference);
reference
}
@ -169,12 +166,12 @@ impl Writer {
Type::GUID => {
let code = self.insert_type_ref("System", "Guid");
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::HRESULT => {
let code = self.insert_type_ref("Windows.Foundation", "HResult");
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::TypeRef(ty) => {
if !ty.generics.is_empty() {
@ -182,7 +179,7 @@ impl Writer {
}
let code = self.insert_type_ref(&ty.namespace, &ty.name);
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
if !ty.generics.is_empty() {
usize_blob(ty.generics.len(), blob);
@ -195,26 +192,26 @@ impl Writer {
Type::BSTR => {
let code = self.insert_type_ref("Windows.Win32.Foundation", "BSTR");
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::IUnknown => {
let code = self.insert_type_ref("Windows.Win32.Foundation", "IUnknown");
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::PCWSTR | Type::PWSTR => {
let code = self.insert_type_ref("Windows.Win32.Foundation", "PWSTR");
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::PCSTR | Type::PSTR => {
let code = self.insert_type_ref("Windows.Win32.Foundation", "PSTR");
blob.push(metadata::ELEMENT_TYPE_VALUETYPE);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::ConstRef(ty) => {
usize_blob(metadata::ELEMENT_TYPE_CMOD_OPT as usize, blob);
usize_blob(self.insert_type_ref("System.Runtime.CompilerServices", "IsConst") as usize, blob);
usize_blob(self.insert_type_ref("System.Runtime.CompilerServices", "IsConst").encode() as usize, blob);
usize_blob(metadata::ELEMENT_TYPE_BYREF as usize, blob);
self.type_blob(ty, blob);
}
@ -237,7 +234,7 @@ impl Writer {
Type::Type => {
let code = self.insert_type_ref("System", "Type");
blob.push(metadata::ELEMENT_TYPE_CLASS);
usize_blob(code as usize, blob);
usize_blob(code.encode() as usize, blob);
}
Type::MutPtr(ty, pointers) | Type::ConstPtr(ty, pointers) => {
for _ in 0..*pointers {
@ -258,7 +255,8 @@ fn round(size: usize, round: usize) -> usize {
(size + round) & !round
}
fn usize_blob(value: usize, blob: &mut Vec<u8>) {
// TODO: need a Blob type for writing
pub fn usize_blob(value: usize, blob: &mut Vec<u8>) {
// See II.23.2 in ECMA-335
assert!(value < 0x20000000);

Просмотреть файл

@ -60,17 +60,15 @@ pub struct ClassLayout {
pub Parent: u32,
}
#[derive(Default)]
pub struct Constant {
pub Type: u16,
pub Parent: u32,
pub Parent: HasConstant,
pub Value: u32,
}
#[derive(Default)]
pub struct CustomAttribute {
pub Parent: u32,
pub Type: u32,
pub Parent: HasAttribute,
pub Type: AttributeType,
pub Value: u32,
}
@ -81,11 +79,10 @@ pub struct Field {
pub Signature: u32,
}
#[derive(Default)]
pub struct GenericParam {
pub Number: u16,
pub Flags: u16,
pub Owner: u32,
pub Owner: TypeOrMethodDef,
pub Name: u32,
}
@ -97,15 +94,13 @@ pub struct ImplMap {
pub ImportScope: u32,
}
#[derive(Default)]
pub struct InterfaceImpl {
pub Class: u32,
pub Interface: u32,
pub Interface: TypeDefOrRef,
}
#[derive(Default)]
pub struct MemberRef {
pub Class: u32,
pub Class: MemberRefParent,
pub Name: u32,
pub Signature: u32,
}
@ -154,19 +149,17 @@ pub struct Property {
pub Type: u32,
}
#[derive(Default)]
pub struct TypeDef {
pub Flags: u32,
pub TypeName: u32,
pub TypeNamespace: u32,
pub Extends: u32,
pub Extends: TypeDefOrRef,
pub FieldList: u32,
pub MethodList: u32,
}
#[derive(Default)]
pub struct TypeRef {
pub ResolutionScope: u32,
pub ResolutionScope: ResolutionScope,
pub TypeName: u32,
pub TypeNamespace: u32,
}
@ -183,12 +176,12 @@ impl Tables {
}
let resolution_scope = metadata::coded_index_size(&[self.Module.len(), self.ModuleRef.len(), self.AssemblyRef.len(), self.TypeRef.len()]);
let type_def_or_ref = metadata::coded_index_size(&[self.TypeDef.len(), self.TypeRef.len(), self.TypeSpec.len()]);
let has_constant = metadata::coded_index_size(&[self.Field.len(), self.Param.len(), self.Property.len()]);
let type_or_method_def = metadata::coded_index_size(&[self.TypeDef.len(), self.MethodDef.len()]);
let member_ref_parent = metadata::coded_index_size(&[self.TypeDef.len(), self.TypeRef.len(), self.ModuleRef.len(), self.MethodDef.len(), self.TypeSpec.len()]);
let custom_attribute_type = metadata::coded_index_size(&[self.MethodDef.len(), self.MemberRef.len(), 0, 0, 0]);
let has_custom_attribute = metadata::coded_index_size(&[self.MethodDef.len(), self.Field.len(), self.TypeRef.len(), self.TypeDef.len(), self.Param.len(), self.InterfaceImpl.len(), self.MemberRef.len(), self.Module.len(), 0, 0, 0, self.ModuleRef.len(), self.TypeSpec.len(), 0, self.AssemblyRef.len(), 0, 0, 0, self.GenericParam.len(), 0, 0]);
let valid_tables: u64 = 1 << 0 | // Module
1 << 0x01 | // TypeRef
@ -254,7 +247,7 @@ impl Tables {
}
for x in self.TypeRef {
buffer.write_code(x.ResolutionScope, resolution_scope);
buffer.write_code(x.ResolutionScope.encode(), resolution_scope);
buffer.write_u32(x.TypeName);
buffer.write_u32(x.TypeNamespace);
}
@ -263,7 +256,7 @@ impl Tables {
buffer.write_u32(x.Flags);
buffer.write_u32(x.TypeName);
buffer.write_u32(x.TypeNamespace);
buffer.write_code(x.Extends, type_def_or_ref);
buffer.write_code(x.Extends.encode(), type_def_or_ref);
buffer.write_index(x.FieldList, self.Field.len());
buffer.write_index(x.MethodList, self.MethodDef.len());
}
@ -291,12 +284,24 @@ impl Tables {
for x in self.InterfaceImpl {
buffer.write_index(x.Class, self.TypeDef.len());
buffer.write_code(x.Interface, type_def_or_ref);
buffer.write_code(x.Interface.encode(), type_def_or_ref);
}
for x in self.MemberRef {
buffer.write_code(x.Class.encode(), member_ref_parent);
buffer.write_u32(x.Name);
buffer.write_u32(x.Signature);
}
for x in self.Constant {
buffer.write_u16(x.Type);
buffer.write_code(x.Parent, has_constant);
buffer.write_code(x.Parent.encode(), has_constant);
buffer.write_u32(x.Value);
}
for x in self.CustomAttribute {
buffer.write_code(x.Parent.encode(), has_custom_attribute);
buffer.write_code(x.Type.encode(), custom_attribute_type);
buffer.write_u32(x.Value);
}
@ -331,7 +336,7 @@ impl Tables {
for x in self.GenericParam {
buffer.write_u16(x.Number);
buffer.write_u16(x.Flags);
buffer.write_code(x.Owner, type_or_method_def);
buffer.write_code(x.Owner.encode(), type_or_method_def);
buffer.write_u32(x.Name);
}

Просмотреть файл

@ -97,6 +97,7 @@ pub enum Value {
EnumDef(TypeDef, Box<Self>),
}
#[derive(Debug)]
pub struct MethodDefSig {
pub call_flags: MethodCallAttributes,
pub return_type: Type,

Просмотреть файл

@ -63,10 +63,13 @@ impl Attribute {
let AttributeType::MemberRef(member) = self.ty();
let mut sig = member.blob(2);
let mut values = self.blob(2);
let _prolog = values.read_u16();
let _this_and_gen_param_count = sig.read_usize();
let prolog = values.read_u16();
std::debug_assert_eq!(prolog, 1);
let this_and_gen_param_count = sig.read_usize();
std::debug_assert_eq!(this_and_gen_param_count, 32);
let fixed_arg_count = sig.read_usize();
let _ret_type = sig.read_usize();
let ret_type = sig.read_usize();
std::debug_assert_eq!(ret_type, 1);
let mut args = Vec::with_capacity(fixed_arg_count);
let reader = self.reader();
@ -219,6 +222,16 @@ impl MemberRef {
pub fn name(&self) -> &'static str {
self.str(1)
}
pub fn signature(&self) -> MethodDefSig {
let reader = self.reader();
let mut blob = self.blob(2);
let call_flags = MethodCallAttributes(blob.read_usize() as u8);
let params = blob.read_usize();
let return_type = reader.type_from_blob(&mut blob, None, &[]);
MethodDefSig { call_flags, return_type, params: (0..params).map(|_| reader.type_from_blob(&mut blob, None, &[])).collect() }
}
}
impl MethodDef {

Просмотреть файл

@ -16,9 +16,9 @@ Options:
);
} else {
match windows_bindgen::bindgen(args) {
Ok(ok) => println!("{}", ok),
Ok(ok) => println!("{ok}"),
Err(error) => {
eprintln!("{}", error);
eprintln!("{error}");
std::process::exit(1);
}
}