Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metadata generation of attributes and constants #2844

Merged
merged 7 commits into from
Feb 13, 2024
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/.vscode
/.vs
/target
/temp
*.lock
*.winmd
4 changes: 2 additions & 2 deletions crates/libs/bindgen/src/rdl/to_winmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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),
});
}
Expand Down
80 changes: 77 additions & 3 deletions crates/libs/bindgen/src/winmd/from_reader.rs
Original file line number Diff line number Diff line change
@@ -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);
Expand All @@ -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,
Expand All @@ -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()),
});
}
Expand All @@ -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
Expand All @@ -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() {
Expand All @@ -85,13 +92,80 @@ 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
// like mdmerge supports for namespace-splitting.
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 {
Expand Down
104 changes: 51 additions & 53 deletions crates/libs/bindgen/src/winmd/writer/codes.rs
Original file line number Diff line number Diff line change
@@ -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),
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,)*
}
}
}
};
}

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,
}
}
code! { AttributeType(3)
(MemberRef, 3)
}

/// 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),
// 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)
}

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,
}
}
code! { HasConstant(2)
(Field, 0)
}

/// 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! { MemberForwarded(1)
(MethodDef, 1)
}

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,
}
}
code! { MemberRefParent(3)
(TypeRef, 1)
}

/// 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),
code! { TypeDefOrRef(2)
(TypeDef, 0)
(TypeRef, 1)
(TypeSpec, 2)
}

impl TypeOrMethodDef {
pub fn encode(&self) -> u32 {
match self {
Self::TypeDef(row) => (row + 1) << 1,
}
impl TypeDefOrRef {
pub fn none() -> Self {
TypeDefOrRef::TypeDef(u32::MAX)
}
}

code! { TypeOrMethodDef(1)
(TypeDef, 0)
}

code! { ResolutionScope(2)
(Module, 0)
(ModuleRef, 1)
(AssemblyRef, 2)
(TypeRef, 3)
}
Loading