Skip to content

Commit

Permalink
Improved riddle syntax parsing and testing (#2324)
Browse files Browse the repository at this point in the history
  • Loading branch information
kennykerr committed Feb 3, 2023
1 parent 61d5d04 commit c5a9492
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 178 deletions.
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

0 comments on commit c5a9492

Please sign in to comment.