2009-05-15 Geoff Norton <gnorton@novell.com>
[mono-project.git] / mono / metadata / monodiet.c
blob1ad1f538819b48207c633503e97fda3414b17d49
1 /*
2 * monodiet.c: an IL code garbage collector
4 * Author:
5 * Paolo Molaro (lupus@ximian.com)
7 * (C) 2004 Novell, Inc.
8 */
10 #include <glib.h>
11 #include <string.h>
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"
22 TODO:
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;
35 enum {
36 TYPE_BASIC = 1 << 0,
37 TYPE_FIELDS = 1 << 1,
38 TYPE_METHODS = 1 << 2,
39 TYPE_PROPERTIES = 1 << 3,
40 TYPE_EVENTS = 1 << 4,
41 TYPE_ALL = TYPE_BASIC | TYPE_FIELDS | TYPE_METHODS | TYPE_PROPERTIES | TYPE_EVENTS
44 static void handle_cattrs (MonoCustomAttrInfo* cattrs);
46 static void
47 add_type (MonoClass* klass)
49 gpointer val = NULL, oldkey = NULL;
50 if (g_hash_table_lookup_extended (type_table, klass, &oldkey, &val))
51 return;
52 g_hash_table_insert (type_table, klass, NULL);
53 g_hash_table_insert (image_table, klass->image, NULL);
56 static void
57 add_types_from_signature (MonoMethodSignature *sig)
59 MonoClass *klass;
60 int i;
61 for (i = 0; i < sig->param_count; ++i) {
62 klass = mono_class_from_mono_type (sig->params [i]);
63 add_type (klass);
65 klass = mono_class_from_mono_type (sig->ret);
66 add_type (klass);
69 static void
70 add_field (MonoClassField *field) {
71 MonoClass *k;
72 MonoCustomAttrInfo* cattrs;
73 gpointer val = NULL, oldkey = NULL;
75 if (g_hash_table_lookup_extended (field_table, field, &oldkey, &val))
76 return;
77 g_hash_table_insert (field_table, field, NULL);
78 add_type (field->parent);
79 k = mono_class_from_mono_type (field->type);
80 add_type (k);
81 cattrs = mono_custom_attrs_from_field (field->parent, field);
82 handle_cattrs (cattrs);
85 static void
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;
91 int i, n;
92 guint32 token;
93 MonoClass *klass;
94 MonoClassField *field;
95 MonoCustomAttrInfo* cattrs;
96 MonoType** locals;
97 gpointer exc_iter;
98 MonoExceptionClause clause;
100 if (g_hash_table_lookup_extended (method_table, method, &oldkey, &val))
101 return;
102 g_hash_table_insert (method_table, method, NULL);
104 g_assert (method->klass);
106 if (verbose > 1)
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))
123 return;
124 if (method->flags & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT))
125 return;
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]);
132 add_type (klass);
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) {
143 if (verbose > 2)
144 g_print ("#%s", mono_disasm_code_one (NULL, method, ip, NULL));
145 if (*ip == 0xfe) {
146 ++ip;
147 i = *ip + 256;
148 } else {
149 i = *ip;
152 opcode = &mono_opcodes [i];
154 switch (opcode->argument) {
155 case MonoInlineNone:
156 ip++;
157 break;
158 case MonoInlineType:
159 token = read32 (ip + 1);
160 add_type (mono_class_get (method->klass->image, token));
161 ip += 5;
162 break;
163 case MonoInlineField: {
164 token = read32 (ip + 1);
165 field = mono_field_from_token (method->klass->image, token, &klass, NULL);
166 add_field (field);
167 add_type (klass);
168 ip += 5;
169 break;
171 case MonoInlineTok:
172 case MonoInlineSig:
173 /* FIXME */
174 case MonoInlineString:
175 case MonoShortInlineR:
176 case MonoInlineBrTarget:
177 case MonoInlineI:
178 ip += 5;
179 break;
180 case MonoInlineVar:
181 ip += 3;
182 break;
183 case MonoShortInlineVar:
184 case MonoShortInlineI:
185 case MonoShortInlineBrTarget:
186 ip += 2;
187 break;
188 case MonoInlineSwitch:
189 ++ip;
190 n = read32 (ip);
191 ip += 4;
192 ip += 4 * n;
193 break;
194 case MonoInlineI8:
195 case MonoInlineR:
196 ip += 9;
197 break;
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);
204 ip += 5;
205 break;
206 default:
207 g_assert_not_reached ();
212 static void
213 handle_cattrs (MonoCustomAttrInfo* cattrs)
215 int i;
216 if (!cattrs)
217 return;
218 for (i = 0; i < cattrs->num_attrs; ++i) {
219 add_types_from_method (cattrs->attrs [i].ctor);
221 mono_custom_attrs_free (cattrs);
224 static void
225 handle_type (MonoClass *klass, guint32 flags)
227 int i;
228 guint32 missing;
229 MonoCustomAttrInfo* cattrs;
230 gpointer val = NULL, oldkey = NULL;
231 MonoProperty* prop;
232 MonoEvent* event;
233 MonoMethod* method;
234 MonoClassField* field;
235 gpointer iter;
237 if (g_hash_table_lookup_extended (type_table, klass, &oldkey, &val)) {
238 missing = flags & ~(GPOINTER_TO_UINT (val));
239 } else {
240 missing = flags;
242 if (!missing)
243 return;
244 g_hash_table_insert (type_table, klass, GUINT_TO_POINTER (missing));
245 if (verbose)
246 g_print ("#processing klass: %s.%s\n", klass->name_space, klass->name);
247 mono_class_init (klass);
248 if (klass->parent)
249 add_type (klass->parent);
250 if (klass->nested_in)
251 add_type (klass->nested_in);
252 iter = NULL;
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)) {
261 iter = NULL;
262 while ((field = mono_class_get_fields (klass, &iter)))
263 add_field (field);
265 iter = NULL;
266 while ((prop = mono_class_get_properties (klass, &iter))) {
267 cattrs = mono_custom_attrs_from_property (klass, prop);
268 handle_cattrs (cattrs);
270 iter = NULL;
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);
281 static void
282 process_image (MonoImage *image, gboolean all) {
283 int i;
284 const MonoTableInfo *t;
285 MonoClass *klass;
286 MonoMethod *entry;
287 guint32 eptoken;
289 if (verbose)
290 g_print ("#processing image: %s\n", mono_image_get_name (image));
291 eptoken = mono_image_get_entry_point (image);
292 if (eptoken) {
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);
299 if (all) {
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);
308 static void
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;
318 static void
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)
323 return;
324 worklist = g_list_prepend (worklist, key);
327 static void
328 check_vmethods (MonoClass *klass, MonoMethod *method)
330 MonoMethod **vtable;
331 if (method->klass == klass)
332 return;
333 mono_class_init (klass);
334 mono_class_init (method->klass);
335 vtable = klass->vtable;
336 /* iface */
337 if (!vtable)
338 return;
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]);
346 } else {
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]);
355 static void
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);
362 do {
363 old_count = new_count;
364 if (verbose)
365 g_print ("#processing type table: %d\n", old_count);
366 g_list_free (worklist);
367 worklist = NULL;
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);
375 worklist = NULL;
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);
386 static void
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)
392 return;
393 /* FIXME: ensure it's the correct token */
394 fprintf (outf, "M:0x%x\n", mono_metadata_token_index (method->token));
397 static void
398 foreach_field (const gpointer key, const gpointer val, gpointer user_data)
400 MonoClassField *field = key;
401 MonoClass *klass = user_data;
402 int idx;
403 if (field->parent != klass)
404 return;
405 idx = mono_metadata_token_index (mono_class_get_field_token (field));
406 fprintf (outf, "F:0x%x\n", idx);
409 static void
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)
415 return;
416 if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR)
417 return;
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);
423 static void
424 foreach_image (const gpointer key, const gpointer val, gpointer user_data)
426 MonoImage *image = key;
427 const char* aname;
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);
434 static void
435 dump_images (const char *filename)
437 if (filename) {
438 FILE* f = fopen (filename, "w");
439 if (!f)
440 g_error ("cannot write to file '%s'\n", filename);
441 else
442 outf = f;
443 } else {
444 outf = stdout;
446 g_hash_table_foreach (image_table, foreach_image, NULL);
447 if (filename)
448 fclose (outf);
451 static void
452 usage (int code) {
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");
463 exit (code);
466 static MonoImage*
467 find_image (char *name)
469 return mono_image_loaded (name);
472 static MonoClass*
473 find_class (MonoImage *image, char *name)
475 char *p = strrchr (name, '.');
476 if (p) {
477 *p = 0;
478 return mono_class_from_name (image, name, p + 1);
480 return mono_class_from_name (image, "", name);
483 static void
484 load_roots (const char* filename)
486 FILE *file;
487 char buf [2048];
488 char *p, *s;
489 int line = 0;
490 MonoImage *image = NULL;
491 MonoClass *klass = NULL;
492 MonoClassField *field;
493 MonoMethodDesc *mdesc;
494 MonoMethod *method;
496 if (!(file = fopen (filename, "r")))
497 return;
499 while (fgets (buf, sizeof (buf), file)) {
500 /* FIXME:
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
506 ++line;
507 s = buf;
508 while (*s && g_ascii_isspace (*s)) ++s;
509 switch (*s) {
510 case 0:
511 case '#':
512 continue; /* comment */
513 case '[':
514 p = strchr (s, ']');
515 if (!p)
516 g_error ("invalid assembly format at line %d\n", line);
517 *p = 0;
518 p = s + 1;
519 image = find_image (p);
520 if (!image)
521 g_error ("image not loaded: %s\n", p);
522 klass = NULL;
523 break;
524 case 'T':
525 if (s [1] != ':')
526 g_error ("invalid type format at line %d\n", line);
527 if (!image)
528 break;
529 klass = find_class (image, s + 2);
530 break;
531 case 'F':
532 if (s [1] != ':')
533 g_error ("invalid field format at line %d\n", line);
534 if (!image || !klass)
535 break;
536 p = s + 2;
537 if (*p == '*') {
538 handle_type (klass, TYPE_FIELDS);
539 break;
541 field = mono_class_get_field_from_name (klass, p);
542 if (!field)
543 g_warning ("no field '%s' at line %d\n", p, line);
544 else
545 add_field (field);
546 break;
547 case 'M':
548 if (s [1] != ':')
549 g_error ("invalid method format at line %d\n", line);
550 if (!image || !klass)
551 break;
552 p = s + 2;
553 if (*p == '*') {
554 handle_type (klass, TYPE_METHODS);
555 break;
557 mdesc = mono_method_desc_new (p, FALSE);
558 if (!mdesc) {
559 g_error ("invalid method desc at line %d\n", line);
561 method = mono_method_desc_search_in_class (mdesc, klass);
562 if (!method)
563 g_warning ("no method '%s' at line %d\n", p, line);
564 else
565 add_types_from_method (method);
566 mono_method_desc_free (mdesc);
567 break;
568 default:
569 g_error ("invalid format at line %d\n", line);
572 fclose (file);
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;
581 int i;
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) {
592 all_meta = FALSE;
593 aname = argv [i];
594 if (strcmp (aname, "-v") == 0) {
595 verbose++;
596 continue;
597 } else if (strcmp (aname, "-e") == 0) {
598 force_enums = 1;
599 continue;
600 } else if (strcmp (aname, "-h") == 0) {
601 usage (0);
602 } else if (strcmp (aname, "-o") == 0) {
603 i++;
604 if (i >= argc)
605 usage (1);
606 outfile = argv [i];
607 continue;
608 } else if (strcmp (aname, "-F") == 0) {
609 i++;
610 if (i >= argc)
611 usage (1);
612 all_meta = TRUE;
613 aname = argv [i];
614 } else if (strcmp (aname, "-a") == 0) {
615 i++;
616 if (i >= argc)
617 usage (1);
618 rootfile = argv [i];
619 continue;
621 assembly = mono_assembly_open (aname, NULL);
622 if (!assembly) {
623 g_print ("cannot open assembly %s\n", aname);
624 exit (1);
626 process_assembly (assembly, all_meta);
628 if (!assembly)
629 usage (1);
630 if (rootfile)
631 load_roots (rootfile);
632 process_images ();
633 dump_images (outfile);
635 return 0;