2 * monodiet.c: an IL code garbage collector
5 * Paolo Molaro (lupus@ximian.com)
7 * (C) 2004 Novell, Inc.
12 #include "mono/metadata/class-internals.h"
13 #include "mono/metadata/assembly.h"
14 #include "mono/metadata/tokentype.h"
15 #include "mono/metadata/opcodes.h"
16 #include "mono/metadata/tabledefs.h"
17 #include "mono/metadata/mono-endian.h"
18 #include "mono/metadata/appdomain.h" /* mono_init */
19 #include "mono/metadata/debug-helpers.h"
23 *) handle proprties, events in a smart way.
24 *) add option that takes a directory and outputs the il files and recompiles automatically
26 static GHashTable
*type_table
;
27 static GHashTable
*method_table
;
28 static GHashTable
*field_table
;
29 static GHashTable
*image_table
;
30 static GList
*virtual_methods
;
31 static int verbose
= 0;
32 static int force_enums
= 0;
33 static FILE *outf
= NULL
;
38 TYPE_METHODS
= 1 << 2,
39 TYPE_PROPERTIES
= 1 << 3,
41 TYPE_ALL
= TYPE_BASIC
| TYPE_FIELDS
| TYPE_METHODS
| TYPE_PROPERTIES
| TYPE_EVENTS
44 static void handle_cattrs (MonoCustomAttrInfo
* cattrs
);
47 add_type (MonoClass
* klass
)
49 gpointer val
= NULL
, oldkey
= NULL
;
50 if (g_hash_table_lookup_extended (type_table
, klass
, &oldkey
, &val
))
52 g_hash_table_insert (type_table
, klass
, NULL
);
53 g_hash_table_insert (image_table
, klass
->image
, NULL
);
57 add_types_from_signature (MonoMethodSignature
*sig
)
61 for (i
= 0; i
< sig
->param_count
; ++i
) {
62 klass
= mono_class_from_mono_type (sig
->params
[i
]);
65 klass
= mono_class_from_mono_type (sig
->ret
);
70 add_field (MonoClassField
*field
) {
72 MonoCustomAttrInfo
* cattrs
;
73 gpointer val
= NULL
, oldkey
= NULL
;
75 if (g_hash_table_lookup_extended (field_table
, field
, &oldkey
, &val
))
77 g_hash_table_insert (field_table
, field
, NULL
);
78 add_type (field
->parent
);
79 k
= mono_class_from_mono_type (field
->type
);
81 cattrs
= mono_custom_attrs_from_field (field
->parent
, field
);
82 handle_cattrs (cattrs
);
86 add_types_from_method (MonoMethod
*method
) {
87 const MonoOpcode
*opcode
;
88 MonoMethodHeader
*header
;
89 const unsigned char *ip
, *il_code_end
;
90 gpointer val
= NULL
, oldkey
= NULL
;
94 MonoClassField
*field
;
95 MonoCustomAttrInfo
* cattrs
;
98 MonoExceptionClause clause
;
100 if (g_hash_table_lookup_extended (method_table
, method
, &oldkey
, &val
))
102 g_hash_table_insert (method_table
, method
, NULL
);
104 g_assert (method
->klass
);
107 g_print ("#processing method: %s\n", mono_method_full_name (method
, TRUE
));
108 mono_class_init (method
->klass
);
109 cattrs
= mono_custom_attrs_from_method (method
);
110 handle_cattrs (cattrs
);
111 add_type (method
->klass
);
112 add_types_from_signature (mono_method_signature (method
));
113 for (i
= 0; i
< mono_method_signature (method
)->param_count
+ 1; ++i
) {
114 cattrs
= mono_custom_attrs_from_param (method
, i
);
115 handle_cattrs (cattrs
);
118 if (method
->flags
& METHOD_ATTRIBUTE_VIRTUAL
)
119 virtual_methods
= g_list_prepend (virtual_methods
, method
);
121 /* if no IL code to parse, return */
122 if (method
->iflags
& (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL
| METHOD_IMPL_ATTRIBUTE_RUNTIME
))
124 if (method
->flags
& (METHOD_ATTRIBUTE_PINVOKE_IMPL
| METHOD_ATTRIBUTE_ABSTRACT
))
127 header
= mono_method_get_header (method
);
129 locals
= mono_method_header_get_locals (header
, &n
, NULL
);
130 for (i
= 0; i
< n
; ++i
) {
131 klass
= mono_class_from_mono_type (locals
[i
]);
134 for (exc_iter
= NULL
; mono_method_header_get_clauses (header
, method
, &exc_iter
, &clause
);) {
135 if (clause
.flags
== MONO_EXCEPTION_CLAUSE_NONE
)
136 add_type (clause
.data
.catch_class
);
139 ip
= mono_method_header_get_code (header
, &n
, NULL
);
140 il_code_end
= ip
+ n
;
142 while (ip
< il_code_end
) {
144 g_print ("#%s", mono_disasm_code_one (NULL
, method
, ip
, NULL
));
152 opcode
= &mono_opcodes
[i
];
154 switch (opcode
->argument
) {
159 token
= read32 (ip
+ 1);
160 add_type (mono_class_get (method
->klass
->image
, token
));
163 case MonoInlineField
: {
164 token
= read32 (ip
+ 1);
165 field
= mono_field_from_token (method
->klass
->image
, token
, &klass
, NULL
);
174 case MonoInlineString
:
175 case MonoShortInlineR
:
176 case MonoInlineBrTarget
:
183 case MonoShortInlineVar
:
184 case MonoShortInlineI
:
185 case MonoShortInlineBrTarget
:
188 case MonoInlineSwitch
:
198 case MonoInlineMethod
:
200 MonoMethod
*cm
= mono_get_method (method
->klass
->image
, read32 (ip
+ 1), NULL
);
201 add_type (cm
->klass
);
202 add_types_from_method (cm
);
207 g_assert_not_reached ();
213 handle_cattrs (MonoCustomAttrInfo
* cattrs
)
218 for (i
= 0; i
< cattrs
->num_attrs
; ++i
) {
219 add_types_from_method (cattrs
->attrs
[i
].ctor
);
221 mono_custom_attrs_free (cattrs
);
225 handle_type (MonoClass
*klass
, guint32 flags
)
229 MonoCustomAttrInfo
* cattrs
;
230 gpointer val
= NULL
, oldkey
= NULL
;
234 MonoClassField
* field
;
237 if (g_hash_table_lookup_extended (type_table
, klass
, &oldkey
, &val
)) {
238 missing
= flags
& ~(GPOINTER_TO_UINT (val
));
244 g_hash_table_insert (type_table
, klass
, GUINT_TO_POINTER (missing
));
246 g_print ("#processing klass: %s.%s\n", klass
->name_space
, klass
->name
);
247 mono_class_init (klass
);
249 add_type (klass
->parent
);
250 if (klass
->nested_in
)
251 add_type (klass
->nested_in
);
253 while ((method
= mono_class_get_methods (klass
, &iter
))) {
254 if ((missing
& TYPE_METHODS
) || strcmp (method
->name
, ".cctor") == 0)
255 add_types_from_method (method
);
257 if (klass
->enumtype
) {
258 add_field (mono_class_get_field_from_name (klass
, "value__"));
260 if (force_enums
|| (missing
& TYPE_FIELDS
)) {
262 while ((field
= mono_class_get_fields (klass
, &iter
)))
266 while ((prop
= mono_class_get_properties (klass
, &iter
))) {
267 cattrs
= mono_custom_attrs_from_property (klass
, prop
);
268 handle_cattrs (cattrs
);
271 while ((event
= mono_class_get_events (klass
, &iter
))) {
272 cattrs
= mono_custom_attrs_from_event (klass
, event
);
273 handle_cattrs (cattrs
);
275 for (i
= 0; i
< klass
->interface_count
; ++i
)
276 add_type (klass
->interfaces
[i
]);
277 cattrs
= mono_custom_attrs_from_class (klass
);
278 handle_cattrs (cattrs
);
282 process_image (MonoImage
*image
, gboolean all
) {
284 const MonoTableInfo
*t
;
290 g_print ("#processing image: %s\n", mono_image_get_name (image
));
291 eptoken
= mono_image_get_entry_point (image
);
293 entry
= mono_get_method (image
, eptoken
, NULL
);
294 add_types_from_method (entry
);
296 /* we always add the <Module> type */
297 klass
= mono_class_get (image
, MONO_TOKEN_TYPE_DEF
| 1);
298 handle_type (klass
, all
? TYPE_ALL
: TYPE_BASIC
);
300 t
= mono_image_get_table_info (image
, MONO_TABLE_TYPEDEF
);
301 for (i
= 1; i
< mono_table_info_get_rows (t
); ++i
) {
302 klass
= mono_class_get (image
, MONO_TOKEN_TYPE_DEF
| (i
+ 1));
303 handle_type (klass
, all
? TYPE_ALL
: TYPE_BASIC
);
309 process_assembly (MonoAssembly
*assembly
, gboolean all
) {
310 MonoCustomAttrInfo
* cattrs
;
311 process_image (mono_assembly_get_image (assembly
), all
);
312 cattrs
= mono_custom_attrs_from_assembly (assembly
);
313 handle_cattrs (cattrs
);
316 static GList
*worklist
= NULL
;
319 collect_type (const gpointer key
, const gpointer val
, gpointer user_data
)
321 MonoClass
*klass
= key
;
322 if (klass
->rank
|| klass
->byval_arg
.type
== MONO_TYPE_PTR
)
324 worklist
= g_list_prepend (worklist
, key
);
328 check_vmethods (MonoClass
*klass
, MonoMethod
*method
)
331 if (method
->klass
== klass
)
333 mono_class_init (klass
);
334 mono_class_init (method
->klass
);
335 vtable
= klass
->vtable
;
339 if (method
->klass
->flags
& TYPE_ATTRIBUTE_INTERFACE
) {
340 if (MONO_CLASS_IMPLEMENTS_INTERFACE (klass
, method
->klass
->interface_id
)) {
341 int iface_offset
= mono_class_interface_offset (klass
, method
->klass
);
342 g_assert (method
->slot
!= -1);
343 if (vtable
[iface_offset
+ method
->slot
])
344 add_types_from_method (vtable
[iface_offset
+ method
->slot
]);
347 if (mono_class_has_parent (klass
, method
->klass
)) {
348 g_assert (method
->slot
!= -1);
349 if (vtable
[method
->slot
])
350 add_types_from_method (vtable
[method
->slot
]);
356 process_images (void) {
357 int old_count
, new_count
;
358 GList
*item
, *vmethod
;
359 new_count
= g_hash_table_size (type_table
);
360 new_count
+= g_hash_table_size (method_table
);
361 new_count
+= g_hash_table_size (field_table
);
363 old_count
= new_count
;
365 g_print ("#processing type table: %d\n", old_count
);
366 g_list_free (worklist
);
368 g_hash_table_foreach (type_table
, collect_type
, NULL
);
369 for (item
= worklist
; item
; item
= item
->next
) {
370 for (vmethod
= virtual_methods
; vmethod
; vmethod
= vmethod
->next
) {
371 check_vmethods (item
->data
, vmethod
->data
);
374 g_list_free (worklist
);
376 g_hash_table_foreach (type_table
, collect_type
, NULL
);
377 for (item
= worklist
; item
; item
= item
->next
) {
378 handle_type (item
->data
, TYPE_BASIC
);
380 new_count
= g_hash_table_size (type_table
);
381 new_count
+= g_hash_table_size (method_table
);
382 new_count
+= g_hash_table_size (field_table
);
383 } while (old_count
!= new_count
);
387 foreach_method (const gpointer key
, const gpointer val
, gpointer user_data
)
389 MonoMethod
*method
= key
;
390 MonoClass
*klass
= user_data
;
391 if (method
->klass
!= klass
)
393 /* FIXME: ensure it's the correct token */
394 fprintf (outf
, "M:0x%x\n", mono_metadata_token_index (method
->token
));
398 foreach_field (const gpointer key
, const gpointer val
, gpointer user_data
)
400 MonoClassField
*field
= key
;
401 MonoClass
*klass
= user_data
;
403 if (field
->parent
!= klass
)
405 idx
= mono_metadata_token_index (mono_class_get_field_token (field
));
406 fprintf (outf
, "F:0x%x\n", idx
);
410 foreach_type (const gpointer key
, const gpointer val
, gpointer user_data
)
412 MonoClass
*klass
= key
;
413 MonoImage
*image
= user_data
;
414 if (klass
->image
!= image
)
416 if (klass
->rank
|| klass
->byval_arg
.type
== MONO_TYPE_PTR
)
418 fprintf (outf
, "T:0x%x\n", mono_metadata_token_index (klass
->type_token
));
419 g_hash_table_foreach (method_table
, foreach_method
, klass
);
420 g_hash_table_foreach (field_table
, foreach_field
, klass
);
424 foreach_image (const gpointer key
, const gpointer val
, gpointer user_data
)
426 MonoImage
*image
= key
;
428 aname
= mono_image_get_name (image
);
429 /* later print the guid as well to prevent mismatches */
430 fprintf (outf
, "[%s]\n", aname
);
431 g_hash_table_foreach (type_table
, foreach_type
, image
);
435 dump_images (const char *filename
)
438 FILE* f
= fopen (filename
, "w");
440 g_error ("cannot write to file '%s'\n", filename
);
446 g_hash_table_foreach (image_table
, foreach_image
, NULL
);
453 printf ("monodiet 0.1 Copyright (c) 2004 Novell, Inc\n");
454 printf ("List the metadata elements used by the named assemblies.\n");
455 printf ("Usage: monodiet [options] assembly [assembly2 ...]\n\n");
456 printf ("Options:\n");
457 printf ("\t-v increase verbose level\n");
458 printf ("\t-h show help screen\n");
459 printf ("\t-e force inclusion of enum members\n");
460 printf ("\t-o FILENAME output the result to filename\n");
461 printf ("\t-a FILENAME add metadata elements from description in filename\n");
462 printf ("\t-F ASSEMBLY force add all metadata elements from assembly\n");
467 find_image (char *name
)
469 return mono_image_loaded (name
);
473 find_class (MonoImage
*image
, char *name
)
475 char *p
= strrchr (name
, '.');
478 return mono_class_from_name (image
, name
, p
+ 1);
480 return mono_class_from_name (image
, "", name
);
484 load_roots (const char* filename
)
490 MonoImage
*image
= NULL
;
491 MonoClass
*klass
= NULL
;
492 MonoClassField
*field
;
493 MonoMethodDesc
*mdesc
;
496 if (!(file
= fopen (filename
, "r")))
499 while (fgets (buf
, sizeof (buf
), file
)) {
501 * decide on the format to use to express types, fields, methods,
502 * maybe the same used on output from the tool, but with explicit
503 * names and signatures instead of token indexes
504 * add wildcard support
508 while (*s
&& g_ascii_isspace (*s
)) ++s
;
512 continue; /* comment */
516 g_error ("invalid assembly format at line %d\n", line
);
519 image
= find_image (p
);
521 g_error ("image not loaded: %s\n", p
);
526 g_error ("invalid type format at line %d\n", line
);
529 klass
= find_class (image
, s
+ 2);
533 g_error ("invalid field format at line %d\n", line
);
534 if (!image
|| !klass
)
538 handle_type (klass
, TYPE_FIELDS
);
541 field
= mono_class_get_field_from_name (klass
, p
);
543 g_warning ("no field '%s' at line %d\n", p
, line
);
549 g_error ("invalid method format at line %d\n", line
);
550 if (!image
|| !klass
)
554 handle_type (klass
, TYPE_METHODS
);
557 mdesc
= mono_method_desc_new (p
, FALSE
);
559 g_error ("invalid method desc at line %d\n", line
);
561 method
= mono_method_desc_search_in_class (mdesc
, klass
);
563 g_warning ("no method '%s' at line %d\n", p
, line
);
565 add_types_from_method (method
);
566 mono_method_desc_free (mdesc
);
569 g_error ("invalid format at line %d\n", line
);
576 main (int argc
, char *argv
[]) {
577 MonoAssembly
*assembly
= NULL
;
578 const char *aname
= NULL
;
579 const char *outfile
= NULL
;
580 const char *rootfile
= NULL
;
582 gboolean all_meta
= FALSE
;
584 mono_init (argv
[0]);
586 type_table
= g_hash_table_new (NULL
, NULL
);
587 method_table
= g_hash_table_new (NULL
, NULL
);
588 field_table
= g_hash_table_new (NULL
, NULL
);
589 image_table
= g_hash_table_new (NULL
, NULL
);
591 for (i
= 1; i
< argc
; ++i
) {
594 if (strcmp (aname
, "-v") == 0) {
597 } else if (strcmp (aname
, "-e") == 0) {
600 } else if (strcmp (aname
, "-h") == 0) {
602 } else if (strcmp (aname
, "-o") == 0) {
608 } else if (strcmp (aname
, "-F") == 0) {
614 } else if (strcmp (aname
, "-a") == 0) {
621 assembly
= mono_assembly_open (aname
, NULL
);
623 g_print ("cannot open assembly %s\n", aname
);
626 process_assembly (assembly
, all_meta
);
631 load_roots (rootfile
);
633 dump_images (outfile
);