| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| /// Types and deserialization logic for IR. See docs in |
| // `rs_bindings_from_cc/ir.h` for more information. |
| use anyhow::Result; |
| use serde::Deserialize; |
| use std::io::Read; |
| |
| pub fn deserialize_ir<R: Read>(reader: R) -> Result<IR> { |
| Ok(serde_json::from_reader(reader)?) |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct HeaderName { |
| pub name: String, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct RsType { |
| pub name: String, |
| pub type_params: Vec<RsType>, |
| pub decl_id: Option<DeclId>, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct CcType { |
| pub name: String, |
| pub is_const: bool, |
| pub type_params: Vec<CcType>, |
| pub decl_id: Option<DeclId>, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct MappedType { |
| pub rs_type: RsType, |
| pub cc_type: CcType, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct Identifier { |
| pub identifier: String, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Deserialize)] |
| #[serde(transparent)] |
| pub struct DeclId(pub usize); |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub enum UnqualifiedIdentifier { |
| Identifier(Identifier), |
| Constructor, |
| Destructor, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub enum ReferenceQualification { |
| LValue, |
| RValue, |
| Unqualified, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct InstanceMethodMetadata { |
| pub reference: ReferenceQualification, |
| pub is_const: bool, |
| pub is_virtual: bool, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct MemberFuncMetadata { |
| pub for_type: Identifier, |
| pub instance_method_metadata: Option<InstanceMethodMetadata>, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct FuncParam { |
| #[serde(rename(deserialize = "type"))] |
| pub type_: MappedType, |
| pub identifier: Identifier, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct Func { |
| pub name: UnqualifiedIdentifier, |
| pub decl_id: DeclId, |
| pub mangled_name: String, |
| pub doc_comment: Option<String>, |
| pub return_type: MappedType, |
| pub params: Vec<FuncParam>, |
| pub is_inline: bool, |
| pub member_func_metadata: Option<MemberFuncMetadata>, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Deserialize)] |
| pub enum AccessSpecifier { |
| Public, |
| Protected, |
| Private, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct Field { |
| pub identifier: Identifier, |
| pub doc_comment: Option<String>, |
| #[serde(rename(deserialize = "type"))] |
| pub type_: MappedType, |
| pub access: AccessSpecifier, |
| pub offset: usize, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub enum SpecialMemberDefinition { |
| Trivial, |
| NontrivialMembers, |
| NontrivialSelf, |
| Deleted, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct SpecialMemberFunc { |
| pub definition: SpecialMemberDefinition, |
| pub access: AccessSpecifier, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct Record { |
| pub identifier: Identifier, |
| pub decl_id: DeclId, |
| pub doc_comment: Option<String>, |
| pub fields: Vec<Field>, |
| pub size: usize, |
| pub alignment: usize, |
| pub copy_constructor: SpecialMemberFunc, |
| pub move_constructor: SpecialMemberFunc, |
| pub destructor: SpecialMemberFunc, |
| pub is_trivial_abi: bool, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct SourceLoc { |
| pub filename: String, |
| pub line: u64, |
| pub column: u64, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct UnsupportedItem { |
| pub name: String, |
| pub message: String, |
| pub source_loc: SourceLoc, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct Comment { |
| pub text: String, |
| } |
| |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub enum Item { |
| Func(Func), |
| Record(Record), |
| UnsupportedItem(UnsupportedItem), |
| Comment(Comment), |
| } |
| |
| impl From<Func> for Item { |
| fn from(func: Func) -> Item { |
| Item::Func(func) |
| } |
| } |
| |
| impl From<Record> for Item { |
| fn from(record: Record) -> Item { |
| Item::Record(record) |
| } |
| } |
| |
| impl From<UnsupportedItem> for Item { |
| fn from(unsupported: UnsupportedItem) -> Item { |
| Item::UnsupportedItem(unsupported) |
| } |
| } |
| |
| impl From<Comment> for Item { |
| fn from(comment: Comment) -> Item { |
| Item::Comment(comment) |
| } |
| } |
| |
| #[derive(Debug, Default, PartialEq, Eq, Hash, Clone, Deserialize)] |
| pub struct IR { |
| #[serde(default)] |
| pub used_headers: Vec<HeaderName>, |
| #[serde(default)] |
| pub items: Vec<Item>, |
| } |
| |
| impl IR { |
| pub fn functions(&self) -> impl Iterator<Item = &Func> { |
| self.items.iter().filter_map(|item| match item { |
| Item::Func(func) => Some(func), |
| _ => None, |
| }) |
| } |
| |
| pub fn records(&self) -> impl Iterator<Item = &Record> { |
| self.items.iter().filter_map(|item| match item { |
| Item::Record(func) => Some(func), |
| _ => None, |
| }) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_used_headers() { |
| let input = r#" |
| { |
| "used_headers": [{ "name": "foo/bar.h" }] |
| } |
| "#; |
| let ir = deserialize_ir(input.as_bytes()).unwrap(); |
| let expected = IR { |
| used_headers: vec![HeaderName { name: "foo/bar.h".to_string() }], |
| ..Default::default() |
| }; |
| assert_eq!(ir, expected); |
| } |
| |
| #[test] |
| fn test_member_access_specifiers() { |
| let input = r#" |
| { |
| "items": [ |
| { "Record" : { |
| "identifier": {"identifier": "SomeStruct" }, |
| "decl_id": 42, |
| "fields": [ |
| { |
| "identifier": {"identifier": "public_int" }, |
| "type": { |
| "rs_type": {"name": "i32", "type_params": []}, |
| "cc_type": {"name": "int", "is_const": false, "type_params": []} |
| }, |
| "access": "Public", |
| "offset": 0 |
| }, |
| { |
| "identifier": {"identifier": "protected_int" }, |
| "type": { |
| "rs_type": {"name": "i32", "type_params": []}, |
| "cc_type": {"name": "int", "is_const": false, "type_params": []} |
| }, |
| "access": "Protected", |
| "offset": 32 |
| }, |
| { |
| "identifier": {"identifier": "private_int" }, |
| "type": { |
| "rs_type": {"name": "i32", "type_params": []}, |
| "cc_type": {"name": "int", "is_const": false, "type_params": []} |
| }, |
| "access": "Private", |
| "offset": 64 |
| } |
| ], |
| "size": 12, |
| "alignment": 4, |
| "copy_constructor": { |
| "definition": "NontrivialSelf", |
| "access": "Private" |
| }, |
| "move_constructor": { |
| "definition": "Deleted", |
| "access": "Protected" |
| }, |
| "destructor": { |
| "definition": "Trivial", |
| "access": "Public" |
| }, |
| "is_trivial_abi": true |
| }} |
| ] |
| } |
| "#; |
| let ir = deserialize_ir(input.as_bytes()).unwrap(); |
| let expected = IR { |
| items: vec![Item::Record(Record { |
| identifier: Identifier { identifier: "SomeStruct".to_string() }, |
| decl_id: DeclId(42), |
| doc_comment: None, |
| fields: vec![ |
| Field { |
| identifier: Identifier { identifier: "public_int".to_string() }, |
| doc_comment: None, |
| type_: MappedType { |
| rs_type: RsType { |
| name: "i32".to_string(), |
| type_params: vec![], |
| decl_id: None, |
| }, |
| cc_type: CcType { |
| name: "int".to_string(), |
| is_const: false, |
| type_params: vec![], |
| decl_id: None, |
| }, |
| }, |
| access: AccessSpecifier::Public, |
| offset: 0, |
| }, |
| Field { |
| identifier: Identifier { identifier: "protected_int".to_string() }, |
| doc_comment: None, |
| type_: MappedType { |
| rs_type: RsType { |
| name: "i32".to_string(), |
| type_params: vec![], |
| decl_id: None, |
| }, |
| cc_type: CcType { |
| name: "int".to_string(), |
| is_const: false, |
| type_params: vec![], |
| decl_id: None, |
| }, |
| }, |
| access: AccessSpecifier::Protected, |
| offset: 32, |
| }, |
| Field { |
| identifier: Identifier { identifier: "private_int".to_string() }, |
| doc_comment: None, |
| type_: MappedType { |
| rs_type: RsType { |
| name: "i32".to_string(), |
| type_params: vec![], |
| decl_id: None, |
| }, |
| cc_type: CcType { |
| name: "int".to_string(), |
| is_const: false, |
| type_params: vec![], |
| decl_id: None, |
| }, |
| }, |
| access: AccessSpecifier::Private, |
| offset: 64, |
| }, |
| ], |
| size: 12, |
| alignment: 4, |
| copy_constructor: SpecialMemberFunc { |
| definition: SpecialMemberDefinition::NontrivialSelf, |
| access: AccessSpecifier::Private, |
| }, |
| move_constructor: SpecialMemberFunc { |
| definition: SpecialMemberDefinition::Deleted, |
| access: AccessSpecifier::Protected, |
| }, |
| destructor: SpecialMemberFunc { |
| definition: SpecialMemberDefinition::Trivial, |
| access: AccessSpecifier::Public, |
| }, |
| is_trivial_abi: true, |
| })], |
| ..Default::default() |
| }; |
| assert_eq!(ir, expected); |
| } |
| |
| #[test] |
| fn test_pointer_member_variable() { |
| let input = r#" |
| { |
| "items": [ |
| { "Record": { |
| "identifier": {"identifier": "SomeStruct" }, |
| "decl_id": 42, |
| "fields": [ |
| { |
| "identifier": {"identifier": "ptr" }, |
| "type": { |
| "rs_type": {"name": "*mut", "type_params": [ |
| {"name": "SomeStruct", "type_params": [], "decl_id": 42} |
| ]}, |
| "cc_type": { "name": "*", "is_const": false, "type_params": [ |
| { |
| "name": "SomeStruct", |
| "is_const": false, |
| "type_params": [], |
| "decl_id": 42 |
| } |
| ]} |
| }, |
| "access": "Public", |
| "offset": 0 |
| } |
| ], |
| "size": 8, |
| "alignment": 8, |
| "copy_constructor": { |
| "definition": "Trivial", |
| "access": "Public" |
| }, |
| "move_constructor": { |
| "definition": "Trivial", |
| "access": "Public" |
| }, |
| "destructor": { |
| "definition": "Trivial", |
| "access": "Public" |
| }, |
| "is_trivial_abi": true |
| }} |
| ] |
| } |
| "#; |
| let ir = deserialize_ir(input.as_bytes()).unwrap(); |
| let expected = IR { |
| items: vec![Item::Record(Record { |
| identifier: Identifier { identifier: "SomeStruct".to_string() }, |
| decl_id: DeclId(42), |
| doc_comment: None, |
| fields: vec![Field { |
| identifier: Identifier { identifier: "ptr".to_string() }, |
| doc_comment: None, |
| type_: MappedType { |
| rs_type: RsType { |
| name: "*mut".to_string(), |
| decl_id: None, |
| type_params: vec![RsType { |
| name: "SomeStruct".to_string(), |
| type_params: vec![], |
| decl_id: Some(DeclId(42)), |
| }], |
| }, |
| cc_type: CcType { |
| name: "*".to_string(), |
| is_const: false, |
| decl_id: None, |
| type_params: vec![CcType { |
| name: "SomeStruct".to_string(), |
| is_const: false, |
| type_params: vec![], |
| decl_id: Some(DeclId(42)), |
| }], |
| }, |
| }, |
| access: AccessSpecifier::Public, |
| offset: 0, |
| }], |
| size: 8, |
| alignment: 8, |
| move_constructor: SpecialMemberFunc { |
| definition: SpecialMemberDefinition::Trivial, |
| access: AccessSpecifier::Public, |
| }, |
| copy_constructor: SpecialMemberFunc { |
| definition: SpecialMemberDefinition::Trivial, |
| access: AccessSpecifier::Public, |
| }, |
| destructor: SpecialMemberFunc { |
| definition: SpecialMemberDefinition::Trivial, |
| access: AccessSpecifier::Public, |
| }, |
| is_trivial_abi: true, |
| })], |
| ..Default::default() |
| }; |
| assert_eq!(ir, expected); |
| } |
| } |