@@ -7,6 +7,7 @@ use goblin::mach::{Mach, MachO, SingleArch};
7
7
use goblin:: pe:: PE ;
8
8
use goblin:: Object ;
9
9
use serde:: Deserialize ;
10
+ use std:: cmp:: Ordering ;
10
11
use std:: collections:: HashMap ;
11
12
use std:: fs;
12
13
use std:: path:: Path ;
@@ -21,19 +22,23 @@ pub fn introspect_cdylib(library_path: impl AsRef<Path>, main_module_name: &str)
21
22
22
23
/// Parses the introspection chunks found in the binary
23
24
fn parse_chunks ( chunks : & [ Chunk ] , main_module_name : & str ) -> Result < Module > {
24
- let chunks_by_id = chunks
25
- . iter ( )
26
- . map ( |c| {
27
- (
28
- match c {
29
- Chunk :: Module { id, .. } => id,
30
- Chunk :: Class { id, .. } => id,
31
- Chunk :: Function { id, .. } => id,
32
- } ,
33
- c,
34
- )
35
- } )
36
- . collect :: < HashMap < _ , _ > > ( ) ;
25
+ let mut chunks_by_id = HashMap :: < & str , & Chunk > :: new ( ) ;
26
+ let mut chunks_by_parent = HashMap :: < & str , Vec < & Chunk > > :: new ( ) ;
27
+ for chunk in chunks {
28
+ if let Some ( id) = match chunk {
29
+ Chunk :: Module { id, .. } => Some ( id) ,
30
+ Chunk :: Class { id, .. } => Some ( id) ,
31
+ Chunk :: Function { id, .. } => id. as_ref ( ) ,
32
+ } {
33
+ chunks_by_id. insert ( id, chunk) ;
34
+ }
35
+ if let Some ( parent) = match chunk {
36
+ Chunk :: Module { .. } | Chunk :: Class { .. } => None ,
37
+ Chunk :: Function { parent, .. } => parent. as_ref ( ) ,
38
+ } {
39
+ chunks_by_parent. entry ( parent) . or_default ( ) . push ( chunk) ;
40
+ }
41
+ }
37
42
// We look for the root chunk
38
43
for chunk in chunks {
39
44
if let Chunk :: Module {
@@ -43,7 +48,7 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
43
48
} = chunk
44
49
{
45
50
if name == main_module_name {
46
- return convert_module ( name, members, & chunks_by_id) ;
51
+ return convert_module ( name, members, & chunks_by_id, & chunks_by_parent ) ;
47
52
}
48
53
}
49
54
}
@@ -53,59 +58,111 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
53
58
fn convert_module (
54
59
name : & str ,
55
60
members : & [ String ] ,
56
- chunks_by_id : & HashMap < & String , & Chunk > ,
61
+ chunks_by_id : & HashMap < & str , & Chunk > ,
62
+ chunks_by_parent : & HashMap < & str , Vec < & Chunk > > ,
57
63
) -> Result < Module > {
64
+ let ( modules, classes, functions) = convert_members (
65
+ & members
66
+ . iter ( )
67
+ . filter_map ( |id| chunks_by_id. get ( id. as_str ( ) ) . copied ( ) )
68
+ . collect :: < Vec < _ > > ( ) ,
69
+ chunks_by_id,
70
+ chunks_by_parent,
71
+ ) ?;
72
+ Ok ( Module {
73
+ name : name. into ( ) ,
74
+ modules,
75
+ classes,
76
+ functions,
77
+ } )
78
+ }
79
+
80
+ /// Convert a list of members of a module or a class
81
+ fn convert_members (
82
+ chunks : & [ & Chunk ] ,
83
+ chunks_by_id : & HashMap < & str , & Chunk > ,
84
+ chunks_by_parent : & HashMap < & str , Vec < & Chunk > > ,
85
+ ) -> Result < ( Vec < Module > , Vec < Class > , Vec < Function > ) > {
58
86
let mut modules = Vec :: new ( ) ;
59
87
let mut classes = Vec :: new ( ) ;
60
88
let mut functions = Vec :: new ( ) ;
61
- for member in members {
62
- if let Some ( chunk) = chunks_by_id. get ( member) {
63
- match chunk {
64
- Chunk :: Module {
89
+ for chunk in chunks {
90
+ match chunk {
91
+ Chunk :: Module {
92
+ name,
93
+ members,
94
+ id : _,
95
+ } => {
96
+ modules. push ( convert_module (
65
97
name,
66
98
members,
67
- id : _,
68
- } => {
69
- modules. push ( convert_module ( name, members, chunks_by_id) ?) ;
70
- }
71
- Chunk :: Class { name, id : _ } => classes. push ( Class { name : name. into ( ) } ) ,
72
- Chunk :: Function {
73
- name,
74
- id : _,
75
- arguments,
76
- } => functions. push ( Function {
99
+ chunks_by_id,
100
+ chunks_by_parent,
101
+ ) ?) ;
102
+ }
103
+ Chunk :: Class { name, id } => {
104
+ let ( _, _, mut methods) = convert_members (
105
+ chunks_by_parent
106
+ . get ( & id. as_str ( ) )
107
+ . map ( Vec :: as_slice)
108
+ . unwrap_or_default ( ) ,
109
+ chunks_by_id,
110
+ chunks_by_parent,
111
+ ) ?;
112
+ // We sort methods to get a stable output
113
+ methods. sort_by ( |l, r| match l. name . cmp ( & r. name ) {
114
+ Ordering :: Equal => {
115
+ // We put the getter before the setter
116
+ if l. decorators . iter ( ) . any ( |d| d == "property" ) {
117
+ Ordering :: Less
118
+ } else if r. decorators . iter ( ) . any ( |d| d == "property" ) {
119
+ Ordering :: Greater
120
+ } else {
121
+ // We pick an ordering based on decorators
122
+ l. decorators . cmp ( & r. decorators )
123
+ }
124
+ }
125
+ o => o,
126
+ } ) ;
127
+ classes. push ( Class {
77
128
name : name. into ( ) ,
78
- arguments : Arguments {
79
- positional_only_arguments : arguments
80
- . posonlyargs
81
- . iter ( )
82
- . map ( convert_argument)
83
- . collect ( ) ,
84
- arguments : arguments. args . iter ( ) . map ( convert_argument) . collect ( ) ,
85
- vararg : arguments
86
- . vararg
87
- . as_ref ( )
88
- . map ( convert_variable_length_argument) ,
89
- keyword_only_arguments : arguments
90
- . kwonlyargs
91
- . iter ( )
92
- . map ( convert_argument)
93
- . collect ( ) ,
94
- kwarg : arguments
95
- . kwarg
96
- . as_ref ( )
97
- . map ( convert_variable_length_argument) ,
98
- } ,
99
- } ) ,
129
+ methods,
130
+ } )
100
131
}
132
+ Chunk :: Function {
133
+ name,
134
+ id : _,
135
+ arguments,
136
+ parent : _,
137
+ decorators,
138
+ } => functions. push ( Function {
139
+ name : name. into ( ) ,
140
+ decorators : decorators. clone ( ) ,
141
+ arguments : Arguments {
142
+ positional_only_arguments : arguments
143
+ . posonlyargs
144
+ . iter ( )
145
+ . map ( convert_argument)
146
+ . collect ( ) ,
147
+ arguments : arguments. args . iter ( ) . map ( convert_argument) . collect ( ) ,
148
+ vararg : arguments
149
+ . vararg
150
+ . as_ref ( )
151
+ . map ( convert_variable_length_argument) ,
152
+ keyword_only_arguments : arguments
153
+ . kwonlyargs
154
+ . iter ( )
155
+ . map ( convert_argument)
156
+ . collect ( ) ,
157
+ kwarg : arguments
158
+ . kwarg
159
+ . as_ref ( )
160
+ . map ( convert_variable_length_argument) ,
161
+ } ,
162
+ } ) ,
101
163
}
102
164
}
103
- Ok ( Module {
104
- name : name. into ( ) ,
105
- modules,
106
- classes,
107
- functions,
108
- } )
165
+ Ok ( ( modules, classes, functions) )
109
166
}
110
167
111
168
fn convert_argument ( arg : & ChunkArgument ) -> Argument {
@@ -290,9 +347,14 @@ enum Chunk {
290
347
name : String ,
291
348
} ,
292
349
Function {
293
- id : String ,
350
+ #[ serde( default ) ]
351
+ id : Option < String > ,
294
352
name : String ,
295
353
arguments : ChunkArguments ,
354
+ #[ serde( default ) ]
355
+ parent : Option < String > ,
356
+ #[ serde( default ) ]
357
+ decorators : Vec < String > ,
296
358
} ,
297
359
}
298
360
0 commit comments