@@ -2,10 +2,51 @@ use proc_macro2::TokenStream;
2
2
use proc_macro2_diagnostics:: { Diagnostic , SpanDiagnosticExt } ;
3
3
use quote:: quote;
4
4
use syn:: spanned:: Spanned ;
5
- use syn:: { parse_macro_input, Data , DeriveInput , Fields , FieldsNamed , Ident } ;
5
+ use syn:: {
6
+ parse_macro_input, Attribute , Data , DeriveInput , Fields , FieldsNamed , Ident , Path , Variant ,
7
+ } ;
6
8
7
9
type Result < T > = std:: result:: Result < T , Diagnostic > ;
8
10
11
+ struct Repr {
12
+ c : bool ,
13
+ primitive : Option < ( Path , bool ) > ,
14
+ }
15
+
16
+ impl Repr {
17
+ fn from_attrs ( attrs : Vec < Attribute > ) -> Result < Self > {
18
+ let mut c = false ;
19
+ let mut primitive = None ;
20
+ for attr in attrs {
21
+ if attr. path ( ) . is_ident ( "repr" ) {
22
+ attr. parse_nested_meta ( |meta| {
23
+ if let Some ( ident) = meta. path . get_ident ( ) {
24
+ if ident == "C" {
25
+ c = true ;
26
+ } else if ident_in_list ( ident, [ "u8" , "u16" , "u32" , "u64" ] ) {
27
+ primitive = Some ( ( meta. path . clone ( ) , false ) ) ;
28
+ } else if ident_in_list ( ident, [ "i8" , "i16" , "i32" , "i64" ] ) {
29
+ primitive = Some ( ( meta. path . clone ( ) , true ) ) ;
30
+ } else if ident_in_list ( ident, [ "usize" , "isize" , "u128" , "i128" ] ) {
31
+ return Err ( ident
32
+ . span ( )
33
+ . error ( format ! ( "`repr({ident})` types are not supported" ) )
34
+ . into ( ) ) ;
35
+ }
36
+ }
37
+ Ok ( ( ) )
38
+ } ) ?;
39
+ }
40
+ }
41
+
42
+ Ok ( Self { c, primitive } )
43
+ }
44
+ }
45
+
46
+ fn ident_in_list < const N : usize > ( ident : & Ident , list : [ & ' static str ; N ] ) -> bool {
47
+ list. iter ( ) . any ( |id| ident == id)
48
+ }
49
+
9
50
#[ proc_macro_derive( AbstractType ) ]
10
51
pub fn abstract_type_derive ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
11
52
let input = parse_macro_input ! ( input as DeriveInput ) ;
@@ -16,21 +57,7 @@ pub fn abstract_type_derive(input: proc_macro::TokenStream) -> proc_macro::Token
16
57
}
17
58
18
59
fn impl_abstract_type ( ast : DeriveInput ) -> Result < TokenStream > {
19
- let mut repr_c = false ;
20
- for attr in ast. attrs {
21
- if attr. path ( ) . is_ident ( "repr" ) {
22
- let _ = attr. parse_nested_meta ( |meta| {
23
- if meta. path . is_ident ( "c" ) {
24
- repr_c = true ;
25
- }
26
- Ok ( ( ) )
27
- } ) ;
28
- }
29
- }
30
-
31
- if !repr_c {
32
- return Err ( ast. ident . span ( ) . error ( "type must be `repr(C)`" ) ) ;
33
- }
60
+ let repr = Repr :: from_attrs ( ast. attrs ) ?;
34
61
35
62
if !ast. generics . params . is_empty ( ) {
36
63
return Err ( ast. generics . span ( ) . error ( "type must not be generic" ) ) ;
@@ -39,7 +66,7 @@ fn impl_abstract_type(ast: DeriveInput) -> Result<TokenStream> {
39
66
let ident = ast. ident ;
40
67
match ast. data {
41
68
Data :: Struct ( s) => match s. fields {
42
- Fields :: Named ( fields) => Ok ( impl_abstract_struct_type ( ident, fields) ) ,
69
+ Fields :: Named ( fields) => impl_abstract_struct_type ( ident, fields, repr ) ,
43
70
Fields :: Unnamed ( _) => Err ( s
44
71
. fields
45
72
. span ( )
@@ -48,8 +75,8 @@ fn impl_abstract_type(ast: DeriveInput) -> Result<TokenStream> {
48
75
. span ( )
49
76
. error ( "unit structs are unsupported; provide at least one named field" ) ) ,
50
77
} ,
51
- Data :: Enum ( _ ) => todo ! ( ) ,
52
- Data :: Union ( u) => Ok ( impl_abstract_union_type ( ident, u. fields ) ) ,
78
+ Data :: Enum ( e ) => impl_abstract_enum_type ( ident , e . variants , repr ) ,
79
+ Data :: Union ( u) => impl_abstract_union_type ( ident, u. fields , repr ) ,
53
80
}
54
81
}
55
82
@@ -72,9 +99,13 @@ fn field_arguments(name: &Ident, fields: FieldsNamed) -> Vec<TokenStream> {
72
99
. collect ( )
73
100
}
74
101
75
- fn impl_abstract_struct_type ( name : Ident , fields : FieldsNamed ) -> TokenStream {
102
+ fn impl_abstract_struct_type ( name : Ident , fields : FieldsNamed , repr : Repr ) -> Result < TokenStream > {
103
+ if !repr. c {
104
+ return Err ( name. span ( ) . error ( "struct must be `repr(C)`" ) ) ;
105
+ }
106
+
76
107
let args = field_arguments ( & name, fields) ;
77
- quote ! {
108
+ Ok ( quote ! {
78
109
impl :: binaryninja:: types:: AbstractType for #name {
79
110
fn resolve_type( ) -> :: binaryninja:: rc:: Ref <:: binaryninja:: types:: Type > {
80
111
:: binaryninja:: types:: Type :: structure(
@@ -84,12 +115,16 @@ fn impl_abstract_struct_type(name: Ident, fields: FieldsNamed) -> TokenStream {
84
115
)
85
116
}
86
117
}
87
- }
118
+ } )
88
119
}
89
120
90
- fn impl_abstract_union_type ( name : Ident , fields : FieldsNamed ) -> TokenStream {
121
+ fn impl_abstract_union_type ( name : Ident , fields : FieldsNamed , repr : Repr ) -> Result < TokenStream > {
122
+ if !repr. c {
123
+ return Err ( name. span ( ) . error ( "union must be `repr(C)`" ) ) ;
124
+ }
125
+
91
126
let args = field_arguments ( & name, fields) ;
92
- quote ! {
127
+ Ok ( quote ! {
93
128
impl :: binaryninja:: types:: AbstractType for #name {
94
129
fn resolve_type( ) -> :: binaryninja:: rc:: Ref <:: binaryninja:: types:: Type > {
95
130
:: binaryninja:: types:: Type :: structure(
@@ -102,5 +137,49 @@ fn impl_abstract_union_type(name: Ident, fields: FieldsNamed) -> TokenStream {
102
137
)
103
138
}
104
139
}
140
+ } )
141
+ }
142
+
143
+ fn impl_abstract_enum_type (
144
+ name : Ident ,
145
+ variants : impl IntoIterator < Item = Variant > ,
146
+ repr : Repr ,
147
+ ) -> Result < TokenStream > {
148
+ if repr. c {
149
+ return Err ( name. span ( ) . error ( "`repr(C)` enums are not supported" ) ) ;
105
150
}
151
+
152
+ let Some ( ( primitive, signed) ) = repr. primitive else {
153
+ return Err ( name
154
+ . span ( )
155
+ . error ( "must provide a primitive `repr` type, e.g. `u32`" ) ) ;
156
+ } ;
157
+ let variants = variants
158
+ . into_iter ( )
159
+ . map ( |variant| {
160
+ if !variant. fields . is_empty ( ) {
161
+ return Err ( variant. span ( ) . error ( "variant must not have any fields" ) ) ;
162
+ }
163
+ let Some ( ( _, discriminant) ) = variant. discriminant else {
164
+ return Err ( variant
165
+ . span ( )
166
+ . error ( "variant must have an explicit discriminant" ) ) ;
167
+ } ;
168
+ let ident = variant. ident ;
169
+ Ok ( quote ! { stringify!( #ident) , #discriminant as u64 } )
170
+ } )
171
+ . collect :: < Result < Vec < _ > > > ( ) ?;
172
+ Ok ( quote ! {
173
+ impl :: binaryninja:: types:: AbstractType for #name {
174
+ fn resolve_type( ) -> :: binaryninja:: rc:: Ref <:: binaryninja:: types:: Type > {
175
+ :: binaryninja:: types:: Type :: enumeration(
176
+ & :: binaryninja:: types:: Enumeration :: builder( )
177
+ #( . insert( #variants) ) *
178
+ . finalize( ) ,
179
+ :: std:: mem:: size_of:: <#primitive>( ) ,
180
+ #signed
181
+ )
182
+ }
183
+ }
184
+ } )
106
185
}
0 commit comments