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

Improved riddle syntax parsing and testing #2324

Merged
merged 12 commits into from
Feb 3, 2023
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
4 changes: 2 additions & 2 deletions crates/libs/metadata/src/writer/imp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str])
Item::Enum(ty) => {
strings.insert(&ty.namespace);
strings.insert(&ty.name);
let enum_type = Type::named(&ty.namespace, &ty.name);
let enum_type = Type::Named((ty.namespace.clone(), ty.name.clone()));
blobs.insert(field_blob(&enum_type, definitions, references));
blobs.insert(field_blob(&value_to_type(&ty.constants[0].value), definitions, references));
ty.constants.iter().for_each(|constant| {
Expand Down Expand Up @@ -138,7 +138,7 @@ pub fn write(name: &str, winrt: bool, definitions: &[Item], assemblies: &[&str])
FieldList: tables.Field.len() as _,
MethodList: tables.MethodDef.len() as _,
});
let enum_type = Type::named(&ty.namespace, &ty.name);
let enum_type = Type::Named((ty.namespace.clone(), ty.name.clone()));
let flags = FieldAttributes::PRIVATE | FieldAttributes::SPECIAL | FieldAttributes::RUNTIME_SPECIAL;
tables.Field.push2(tables::Field {
Flags: flags.0,
Expand Down
48 changes: 0 additions & 48 deletions crates/libs/metadata/src/writer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,82 +18,40 @@ pub struct Struct {
pub fields: Vec<Field>,
}

impl Struct {
pub fn item(namespace: &str, name: &str, fields: Vec<Field>) -> Item {
Item::Struct(Self { namespace: namespace.to_string(), name: name.to_string(), fields })
}
}

pub struct Enum {
pub namespace: String,
pub name: String,
pub constants: Vec<Constant>,
}

impl Enum {
pub fn item(namespace: &str, name: &str, constants: Vec<Constant>) -> Item {
Item::Enum(Self { namespace: namespace.to_string(), name: name.to_string(), constants })
}
}

pub struct Interface {
pub namespace: String,
pub name: String,
pub methods: Vec<Method>,
}

impl Interface {
pub fn item(namespace: &str, name: &str, methods: Vec<Method>) -> Item {
Item::Interface(Self { namespace: namespace.to_string(), name: name.to_string(), methods })
}
}

pub struct Field {
pub name: String,
pub ty: Type,
}

impl Field {
pub fn new(name: &str, ty: Type) -> Self {
Self { name: name.to_string(), ty }
}
}

pub struct Constant {
pub name: String,
pub value: Value,
}

impl Constant {
pub fn new(name: &str, value: Value) -> Self {
Self { name: name.to_string(), value }
}
}

pub struct Method {
pub name: String,
pub return_type: Type,
pub params: Vec<Param>,
}

impl Method {
pub fn new(name: &str, return_type: Type, params: Vec<Param>) -> Self {
Self { name: name.to_string(), return_type, params }
}
}

pub struct Param {
pub name: String,
pub ty: Type,
pub flags: ParamFlags,
}

impl Param {
pub fn new(name: &str, ty: Type, flags: ParamFlags) -> Self {
Self { name: name.to_string(), ty, flags }
}
}

flags!(ParamFlags, u32);
impl ParamFlags {
pub const INPUT: Self = Self(0x1);
Expand Down Expand Up @@ -121,12 +79,6 @@ pub enum Type {
Named((String, String)),
}

impl Type {
pub fn named(namespace: &str, name: &str) -> Self {
Self::Named((namespace.to_string(), name.to_string()))
}
}

pub enum Value {
Bool(bool),
U8(u8),
Expand Down
22 changes: 0 additions & 22 deletions crates/tests/metadata/tests/writer.rs

This file was deleted.

20 changes: 20 additions & 0 deletions crates/tests/riddle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
use std::process::Command;

pub fn run_riddle(input: &str) -> String {
let mut command = Command::new("cargo.exe");
command.arg("install").arg("--path").arg("../../tools/riddle");

if !command.status().unwrap().success() {
panic!("Failed to install riddle");
}

let output = std::env::temp_dir().join(std::path::Path::new(input).with_extension("winmd").file_name().expect("file_name failed")).to_string_lossy().into_owned();

let mut command = Command::new("riddle.exe");
command.arg("-input").arg(input).arg("-output").arg(&output);

if !command.status().unwrap().success() {
panic!("Failed to run riddle");
}

output
}
File renamed without changes.
22 changes: 3 additions & 19 deletions crates/tests/riddle/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,9 @@
use std::process::Command;
use test_riddle::run_riddle;
use windows_metadata::reader::*;

#[test]
fn basic() {
// For visual inspection: ildasm.exe %temp%\test_riddle.winmd
let output = std::env::temp_dir().join("test_riddle.winmd").to_string_lossy().into_owned();

let mut command = Command::new("cargo.exe");
command.arg("install").arg("--path").arg("../../tools/riddle");

if !command.status().unwrap().success() {
panic!("Failed to install riddle");
}

let mut command = Command::new("riddle.exe");
command.arg("-input").arg("src/basic.rs").arg("-output").arg(&output);

if !command.status().unwrap().success() {
panic!("Failed to run riddle");
}

fn riddle_basic() {
let output = run_riddle("tests/basic.idl");
let files = File::with_default(&[&output]).expect("Failed to open winmd files");
let reader = &Reader::new(&files);

Expand Down
8 changes: 8 additions & 0 deletions crates/tests/riddle/tests/enum.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod TypeNamespace {
enum TypeName {
A,
B,
C = 42,
D,
}
}
26 changes: 26 additions & 0 deletions crates/tests/riddle/tests/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use test_riddle::run_riddle;
use windows_metadata::reader::*;

#[test]
fn riddle_enum() {
let output = run_riddle("tests/enum.idl");
let files = File::with_default(&[&output]).expect("Failed to open winmd files");
let reader = &Reader::new(&files);

let def = reader.get(TypeName::new("TypeNamespace", "TypeName")).next().expect("Type missing");
assert_eq!(reader.type_def_kind(def), TypeKind::Enum);

let fields: Vec<Field> = reader.type_def_fields(def).collect();
assert_eq!(fields.len(), 5);
assert_eq!(reader.field_name(fields[0]), "value__");

assert_eq!(reader.field_name(fields[1]), "A");
assert_eq!(reader.field_name(fields[2]), "B");
assert_eq!(reader.field_name(fields[3]), "C");
assert_eq!(reader.field_name(fields[4]), "D");

assert!(matches!(reader.constant_value(reader.field_constant(fields[1]).expect("missing constant")), Value::I32(value) if value == 0));
assert!(matches!(reader.constant_value(reader.field_constant(fields[2]).expect("missing constant")), Value::I32(value) if value == 1));
assert!(matches!(reader.constant_value(reader.field_constant(fields[3]).expect("missing constant")), Value::I32(value) if value == 42));
assert!(matches!(reader.constant_value(reader.field_constant(fields[4]).expect("missing constant")), Value::I32(value) if value == 43));
}
8 changes: 8 additions & 0 deletions crates/tests/riddle/tests/struct.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
mod TypeNamespace {
struct TypeName{
a: i32,
b: f32,
c: u64,
d: f64,
}
}
25 changes: 25 additions & 0 deletions crates/tests/riddle/tests/struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use test_riddle::run_riddle;
use windows_metadata::reader::*;

#[test]
fn riddle_struct() {
let output = run_riddle("tests/struct.idl");
let files = File::with_default(&[&output]).expect("Failed to open winmd files");
let reader = &Reader::new(&files);

let def = reader.get(TypeName::new("TypeNamespace", "TypeName")).next().expect("Type missing");
assert_eq!(reader.type_def_kind(def), TypeKind::Struct);

let fields: Vec<Field> = reader.type_def_fields(def).collect();
assert_eq!(fields.len(), 4);

assert_eq!(reader.field_name(fields[0]), "a");
assert_eq!(reader.field_name(fields[1]), "b");
assert_eq!(reader.field_name(fields[2]), "c");
assert_eq!(reader.field_name(fields[3]), "d");

assert!(matches!(reader.field_type(fields[0], None), Type::I32));
assert!(matches!(reader.field_type(fields[1], None), Type::F32));
assert!(matches!(reader.field_type(fields[2], None), Type::U64));
assert!(matches!(reader.field_type(fields[3], None), Type::F64));
}
93 changes: 6 additions & 87 deletions crates/tools/riddle/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,90 +1,8 @@
mod syntax;
use metadata::writer;
use std::io::Read;
use syn::{parse::*, *};

mod keywords {
syn::custom_keyword!(interface);
}

#[derive(Debug)]
struct Module {
pub name: Ident,
pub members: Vec<ModuleMember>,
}

impl Parse for Module {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Token![mod]>()?;
let name = input.parse::<Ident>()?;
let content;
braced!(content in input);
let mut members = Vec::new();
while !content.is_empty() {
members.push(content.parse::<ModuleMember>()?);
}
Ok(Self { name, members })
}
}

#[derive(Debug)]
enum ModuleMember {
Module(Module),
Interface(Interface),
}

impl Parse for ModuleMember {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(Token![mod]) {
Ok(ModuleMember::Module(input.parse::<Module>()?))
} else if lookahead.peek(keywords::interface) {
Ok(ModuleMember::Interface(input.parse::<Interface>()?))
} else {
Err(lookahead.error())
}
}
}

#[derive(Debug)]
struct Interface {
pub name: Ident,
pub methods: Vec<TraitItemMethod>,
}

impl Parse for Interface {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<keywords::interface>()?;
let name = input.parse::<Ident>()?;
let content;
braced!(content in input);
let mut methods = Vec::new();
while !content.is_empty() {
methods.push(content.parse::<TraitItemMethod>()?);
}
Ok(Self { name, methods })
}
}

fn module_to_writer(namespace: &str, module: &Module, items: &mut Vec<writer::Item>) -> Result<()> {
for member in &module.members {
match member {
ModuleMember::Module(module) => module_to_writer(&format!("{namespace}.{}", module.name), module, items)?,
ModuleMember::Interface(interface) => interface_to_writer(namespace, interface, items)?,
}
}
Ok(())
}

fn interface_to_writer(namespace: &str, interface: &Interface, items: &mut Vec<writer::Item>) -> Result<()> {
let mut methods = Vec::new();

for method in &interface.methods {
methods.push(writer::Method { name: method.sig.ident.to_string(), return_type: writer::Type::Void, params: vec![] });
}

items.push(writer::Interface::item(namespace, &interface.name.to_string(), methods));
Ok(())
}
use syn::*;
use syntax::*;

fn main() {
if let Err(message) = run() {
Expand Down Expand Up @@ -147,9 +65,10 @@ fn run() -> ToolResult {
let mut source = String::new();
file.read_to_string(&mut source).map_err(|_| format!("failed to read `{filename}`"))?;

if let Err(error) = parse_str::<Module>(&source).and_then(|module| module_to_writer(&module.name.to_string(), &module, &mut items)) {
if let Err(error) = parse_str::<Module>(&source).and_then(|module| module.to_writer(module.name.to_string(), &mut items)) {
let start = error.span().start();
return Err(format!("{error}\n --> {}:{:?}:{:?} ", filename, start.line, start.column));
let filename = std::fs::canonicalize(filename).map_err(|_| format!("failed to canonicalize `{filename}`"))?;
return Err(format!("{error}\n --> {}:{:?}:{:?} ", filename.to_string_lossy().trim_start_matches(r#"\\?\"#), start.line, start.column));
}
}

Expand Down
Loading