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;
90 gpointer val = NULL, oldkey = NULL;
94 MonoClassField *field;
95 MonoCustomAttrInfo* cattrs;
97 if (g_hash_table_lookup_extended (method_table, method, &oldkey, &val))
99 g_hash_table_insert (method_table, method, NULL);
101 g_assert (method->klass);
104 g_print ("#processing method: %s\n", mono_method_full_name (method, TRUE));
105 mono_class_init (method->klass);
106 cattrs = mono_custom_attrs_from_method (method);
107 handle_cattrs (cattrs);
108 add_type (method->klass);
109 add_types_from_signature (method->signature);
110 for (i = 0; i < method->signature->param_count + 1; ++i) {
111 cattrs = mono_custom_attrs_from_param (method, i);
112 handle_cattrs (cattrs);
115 if (method->flags & METHOD_ATTRIBUTE_VIRTUAL)
116 virtual_methods = g_list_prepend (virtual_methods, method);
118 /* if no IL code to parse, return */
119 if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME))
121 if (method->flags & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT))
124 header = mono_method_get_header (method);
126 for (i = 0; i < header->num_locals; ++i) {
127 klass = mono_class_from_mono_type (header->locals [i]);
130 for (i = 0; i < header->num_clauses; ++i) {
131 MonoExceptionClause *clause = &header->clauses [i];
132 if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE)
133 add_type (clause->data.catch_class);
138 while (ip < (header->code + header->code_size)) {
140 g_print ("#%s", mono_disasm_code_one (NULL, method, ip, NULL));
148 opcode = &mono_opcodes [i];
150 switch (opcode->argument) {
155 token = read32 (ip + 1);
156 add_type (mono_class_get (method->klass->image, token));
159 case MonoInlineField: {
160 token = read32 (ip + 1);
161 field = mono_field_from_token (method->klass->image, token, &klass, NULL);
170 case MonoInlineString:
171 case MonoShortInlineR:
172 case MonoInlineBrTarget:
179 case MonoShortInlineVar:
180 case MonoShortInlineI:
181 case MonoShortInlineBrTarget:
184 case MonoInlineSwitch:
194 case MonoInlineMethod:
196 MonoMethod *cm = mono_get_method (method->klass->image, read32 (ip + 1), NULL);
197 add_type (cm->klass);
198 add_types_from_method (cm);
203 g_assert_not_reached ();
209 handle_cattrs (MonoCustomAttrInfo* cattrs)
214 for (i = 0; i < cattrs->num_attrs; ++i) {
215 add_types_from_method (cattrs->attrs [i].ctor);
220 handle_type (MonoClass *klass, guint32 flags)
224 MonoCustomAttrInfo* cattrs;
225 gpointer val = NULL, oldkey = NULL;
227 if (g_hash_table_lookup_extended (type_table, klass, &oldkey, &val)) {
228 missing = flags & ~(GPOINTER_TO_UINT (val));
234 g_hash_table_insert (type_table, klass, GUINT_TO_POINTER (missing));
236 g_print ("#processing klass: %s.%s\n", klass->name_space, klass->name);
237 mono_class_init (klass);
239 add_type (klass->parent);
240 if (klass->nested_in)
241 add_type (klass->nested_in);
242 for (i = 0; i < klass->method.count; ++i) {
243 if ((missing & TYPE_METHODS) || strcmp (klass->methods [i]->name, ".cctor") == 0)
244 add_types_from_method (klass->methods [i]);
246 if (klass->enumtype) {
247 add_field (mono_class_get_field_from_name (klass, "value__"));
249 if (force_enums || (missing & TYPE_FIELDS)) {
250 for (i = 0; i < klass->field.count; ++i) {
251 add_field (&klass->fields [i]);
254 for (i = 0; i < klass->property.count; ++i) {
255 cattrs = mono_custom_attrs_from_property (klass, &klass->properties [i]);
256 handle_cattrs (cattrs);
258 for (i = 0; i < klass->event.count; ++i) {
259 cattrs = mono_custom_attrs_from_event (klass, &klass->events [i]);
260 handle_cattrs (cattrs);
262 for (i = 0; i < klass->interface_count; ++i)
263 add_type (klass->interfaces [i]);
264 cattrs = mono_custom_attrs_from_class (klass);
265 handle_cattrs (cattrs);
269 process_image (MonoImage *image, gboolean all) {
271 const MonoTableInfo *t;
277 g_print ("#processing image: %s\n", mono_image_get_name (image));
278 eptoken = mono_image_get_entry_point (image);
280 entry = mono_get_method (image, eptoken, NULL);
281 add_types_from_method (entry);
283 /* we always add the <Module> type */
284 klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | 1);
285 handle_type (klass, all? TYPE_ALL: TYPE_BASIC);
287 t = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
288 for (i = 1; i < mono_table_info_get_rows (t); ++i) {
289 klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | (i + 1));
290 handle_type (klass, all? TYPE_ALL: TYPE_BASIC);
296 process_assembly (MonoAssembly *assembly, gboolean all) {
297 MonoCustomAttrInfo* cattrs;
298 process_image (mono_assembly_get_image (assembly), all);
299 cattrs = mono_custom_attrs_from_assembly (assembly);
300 handle_cattrs (cattrs);
303 static GList *worklist = NULL;
306 collect_type (const gpointer key, const gpointer val, gpointer user_data)
308 MonoClass *klass = key;
309 if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR)
311 worklist = g_list_prepend (worklist, key);
315 check_vmethods (MonoClass *klass, MonoMethod *method)
318 if (method->klass == klass)
320 mono_class_init (klass);
321 mono_class_init (method->klass);
322 vtable = klass->vtable;
326 if (method->klass->flags & TYPE_ATTRIBUTE_INTERFACE) {
327 if (method->klass->interface_id <= klass->max_interface_id &&
328 (klass->interface_offsets [method->klass->interface_id] >= 0)) {
329 int iface_offset = klass->interface_offsets [method->klass->interface_id];
330 g_assert (method->slot != -1);
331 if (vtable [iface_offset + method->slot])
332 add_types_from_method (vtable [iface_offset + method->slot]);
335 if (mono_class_has_parent (klass, method->klass)) {
336 g_assert (method->slot != -1);
337 if (vtable [method->slot])
338 add_types_from_method (vtable [method->slot]);
344 process_images (void) {
345 int old_count, new_count;
346 GList *item, *vmethod;
347 new_count = g_hash_table_size (type_table);
348 new_count += g_hash_table_size (method_table);
349 new_count += g_hash_table_size (field_table);
351 old_count = new_count;
353 g_print ("#processing type table: %d\n", old_count);
354 g_list_free (worklist);
356 g_hash_table_foreach (type_table, collect_type, NULL);
357 for (item = worklist; item; item = item->next) {
358 for (vmethod = virtual_methods; vmethod; vmethod = vmethod->next) {
359 check_vmethods (item->data, vmethod->data);
362 g_list_free (worklist);
364 g_hash_table_foreach (type_table, collect_type, NULL);
365 for (item = worklist; item; item = item->next) {
366 handle_type (item->data, TYPE_BASIC);
368 new_count = g_hash_table_size (type_table);
369 new_count += g_hash_table_size (method_table);
370 new_count += g_hash_table_size (field_table);
371 } while (old_count != new_count);
375 foreach_method (const gpointer key, const gpointer val, gpointer user_data)
377 MonoMethod *method = key;
378 MonoClass *klass = user_data;
379 if (method->klass != klass)
381 /* FIXME: ensure it's the correct token */
382 fprintf (outf, "M:0x%x\n", mono_metadata_token_index (method->token));
386 foreach_field (const gpointer key, const gpointer val, gpointer user_data)
388 MonoClassField *field = key;
389 MonoClass *klass = user_data;
391 if (field->parent != klass)
393 idx = mono_metadata_token_index (mono_class_get_field_token (field));
394 fprintf (outf, "F:0x%x\n", idx);
398 foreach_type (const gpointer key, const gpointer val, gpointer user_data)
400 MonoClass *klass = key;
401 MonoImage *image = user_data;
402 if (klass->image != image)
404 if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR)
406 fprintf (outf, "T:0x%x\n", mono_metadata_token_index (klass->type_token));
407 g_hash_table_foreach (method_table, foreach_method, klass);
408 g_hash_table_foreach (field_table, foreach_field, klass);
412 foreach_image (const gpointer key, const gpointer val, gpointer user_data)
414 MonoImage *image = key;
416 aname = mono_image_get_name (image);
417 /* later print the guid as well to prevent mismatches */
418 fprintf (outf, "[%s]\n", aname);
419 g_hash_table_foreach (type_table, foreach_type, image);
423 dump_images (const char *filename)
426 FILE* f = fopen (filename, "w");
428 g_error ("cannot write to file '%s'\n", filename);
434 g_hash_table_foreach (image_table, foreach_image, NULL);
441 printf ("monodiet 0.1 Copyright (c) 2004 Novell, Inc\n");
442 printf ("List the metadata elements used by the named assemblies.\n");
443 printf ("Usage: monodiet [options] assembly [assembly2 ...]\n\n");
444 printf ("Options:\n");
445 printf ("\t-v increase verbose level\n");
446 printf ("\t-h show help screen\n");
447 printf ("\t-e force inclusion of enum members\n");
448 printf ("\t-o FILENAME output the result to filename\n");
449 printf ("\t-a FILENAME add metadata elements from description in filename\n");
450 printf ("\t-F ASSEMBLY force add all metadata elements from assembly\n");
455 find_image (char *name)
457 return mono_image_loaded (name);
461 find_class (MonoImage *image, char *name)
463 char *p = strrchr (name, '.');
466 return mono_class_from_name (image, name, p + 1);
468 return mono_class_from_name (image, "", name);
472 load_roots (const char* filename)
478 MonoImage *image = NULL;
479 MonoClass *klass = NULL;
480 MonoClassField *field;
481 MonoMethodDesc *mdesc;
484 if (!(file = fopen (filename, "r")))
487 while (fgets (buf, sizeof (buf), file)) {
489 * decide on the format to use to express types, fields, methods,
490 * maybe the same used on output from the tool, but with explicit
491 * names and signatures instead of token indexes
492 * add wildcard support
496 while (*s && g_ascii_isspace (*s)) ++s;
500 continue; /* comment */
504 g_error ("invalid assembly format at line %d\n", line);
507 image = find_image (p);
509 g_error ("image not loaded: %s\n", p);
514 g_error ("invalid type format at line %d\n", line);
517 klass = find_class (image, s + 2);
521 g_error ("invalid field format at line %d\n", line);
522 if (!image || !klass)
526 handle_type (klass, TYPE_FIELDS);
529 field = mono_class_get_field_from_name (klass, p);
531 g_warning ("no field '%s' at line %d\n", p, line);
537 g_error ("invalid method format at line %d\n", line);
538 if (!image || !klass)
542 handle_type (klass, TYPE_METHODS);
545 mdesc = mono_method_desc_new (p, FALSE);
547 g_error ("invalid method desc at line %d\n", line);
549 method = mono_method_desc_search_in_class (mdesc, klass);
551 g_warning ("no method '%s' at line %d\n", p, line);
553 add_types_from_method (method);
554 mono_method_desc_free (mdesc);
557 g_error ("invalid format at line %d\n", line);
564 main (int argc, char *argv[]) {
565 MonoAssembly *assembly = NULL;
566 const char *aname = NULL;
567 const char *outfile = NULL;
568 const char *rootfile = NULL;
570 gboolean all_meta = FALSE;
572 mono_init (argv [0]);
574 type_table = g_hash_table_new (NULL, NULL);
575 method_table = g_hash_table_new (NULL, NULL);
576 field_table = g_hash_table_new (NULL, NULL);
577 image_table = g_hash_table_new (NULL, NULL);
579 for (i = 1; i < argc; ++i) {
582 if (strcmp (aname, "-v") == 0) {
585 } else if (strcmp (aname, "-e") == 0) {
588 } else if (strcmp (aname, "-h") == 0) {
590 } else if (strcmp (aname, "-o") == 0) {
596 } else if (strcmp (aname, "-F") == 0) {
602 } else if (strcmp (aname, "-a") == 0) {
609 assembly = mono_assembly_open (aname, NULL);
611 g_print ("cannot open assembly %s\n", aname);
614 process_assembly (assembly, all_meta);
619 load_roots (rootfile);
621 dump_images (outfile);