Thu Sep 27 19:52:11 CEST 2001 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / metadata / class.c
1 /*
2  * class.c: Class management for the Mono runtime
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  *
9  * Possible Optimizations:
10  *     in mono_class_create, do not allocate the class right away,
11  *     but wait until you know the size of the FieldMap, so that
12  *     the class embeds directly the FieldMap after the vtable.
13  *
14  * 
15  */
16 #include <config.h>
17 #include <glib.h>
18 #include <stdio.h>
19 #include <mono/metadata/image.h>
20 #include <mono/metadata/cil-coff.h>
21 #include <mono/metadata/metadata.h>
22 #include <mono/metadata/tabledefs.h>
23 #include <mono/metadata/tokentype.h>
24 #include <mono/metadata/class.h>
25 #include <mono/metadata/object.h>
26
27 #define CSIZE(x) (sizeof (x) / 4)
28
29 static MonoClass *
30 mono_class_create_from_typeref (MonoImage *image, guint32 type_token)
31 {
32         guint32 cols [MONO_TYPEREF_SIZE];
33         MonoTableInfo  *t = &image->tables [MONO_TABLE_TYPEREF];
34         guint32 idx;
35         const char *name, *nspace;
36         MonoClass *res;
37
38         mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, MONO_TYPEREF_SIZE);
39         g_assert ((cols [MONO_TYPEREF_SCOPE] & 0x3) == 2);
40         idx = cols [MONO_TYPEREF_SCOPE] >> 2;
41
42         if (!image->references ||  !image->references [idx-1]) {
43                 /* 
44                  * detected a reference to mscorlib, we simply return a reference to a dummy 
45                  * until we have a better solution.
46                  */
47                 res = mono_class_from_name (image, "System", "MonoDummy");
48                 /* prevent method loading */
49                 res->dummy = 1;
50                 /* some storage if the type is used  - very ugly hack */
51                 res->instance_size = 2*sizeof (gpointer);
52                 return res;
53         }       
54
55         name = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAME]);
56         nspace = mono_metadata_string_heap (image, cols [MONO_TYPEREF_NAMESPACE]);
57         
58         /* load referenced assembly */
59         image = image->references [idx-1]->image;
60
61         return mono_class_from_name (image, nspace, name);
62 }
63
64 /** 
65  * class_compute_field_layout:
66  * @m: pointer to the metadata.
67  * @class: The class to initialize
68  *
69  * Initializes the class->fields.
70  *
71  * Currently we only support AUTO_LAYOUT, and do not even try to do
72  * a good job at it.  This is temporary to get the code for Paolo.
73  */
74 static void
75 class_compute_field_layout (MonoClass *class)
76 {
77         MonoImage *m = class->image; 
78         const int top = class->field.count;
79         guint32 layout = class->flags & TYPE_ATTRIBUTE_LAYOUT_MASK;
80         MonoTableInfo *t = &m->tables [MONO_TABLE_FIELD];
81         int i, j;
82
83         /*
84          * Fetch all the field information.
85          */
86         for (i = 0; i < top; i++){
87                 const char *sig;
88                 guint32 cols [3];
89                 int idx = class->field.first + i;
90                 
91                 mono_metadata_decode_row (t, idx, cols, CSIZE (cols));
92                 sig = mono_metadata_blob_heap (m, cols [2]);
93                 mono_metadata_decode_value (sig, &sig);
94                 /* FIELD signature == 0x06 */
95                 g_assert (*sig == 0x06);
96                 class->fields [i].type = mono_metadata_parse_field_type (
97                         m, cols [MONO_FIELD_FLAGS], sig + 1, &sig);
98         }
99         /*
100          * Compute field layout and total size.
101          */
102         switch (layout){
103         case TYPE_ATTRIBUTE_AUTO_LAYOUT:
104         case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT:
105                 for (i = 0; i < top; i++){
106                         int size, align;
107                         
108                         size = mono_type_size (class->fields [i].type, &align);
109                         if (class->fields [i].type->attrs & FIELD_ATTRIBUTE_STATIC) {
110                                 class->fields [i].offset = class->class_size;
111                                 class->class_size += (class->class_size % align);
112                                 class->class_size += size;
113                         } else {
114                                 class->fields [i].offset = class->instance_size;
115                                 class->instance_size += (class->instance_size % align);
116                                 class->instance_size += size;
117                         }
118                 }
119                 break;
120         case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT:
121                 for (i = 0; i < top; i++){
122                         guint32 cols [2];
123                         int size, align;
124                         int idx = class->field.first + i;
125
126                         t = &m->tables [MONO_TABLE_FIELDLAYOUT];
127
128                         for (j = 0; j < t->rows; j++) {
129
130                                 mono_metadata_decode_row (t, j, cols, CSIZE (cols));
131                                 if (cols [1] == idx) {
132                                         g_warning ("TODO: Explicit layout not supported yet");
133                                 }
134                         }
135                         
136                         size = mono_type_size (class->fields [i].type, &align);
137                         if (class->fields [i].type->attrs & FIELD_ATTRIBUTE_STATIC) {
138                                 class->fields [i].offset = class->class_size;
139                                 class->class_size += (class->class_size % align);
140                                 class->class_size += size;
141                         } else {
142                                 class->fields [i].offset = class->instance_size;
143                                 class->instance_size += (class->instance_size % align);
144                                 class->instance_size += size;
145                         }
146                 }
147                 break;
148         }
149 }
150
151 void
152 mono_class_metadata_init (MonoClass *class)
153 {
154         int i;
155
156         if (class->metadata_inited)
157                 return;
158
159         if (class->parent) {
160                 if (!class->parent->metadata_inited)
161                         mono_class_metadata_init (class->parent);
162                 class->instance_size = class->parent->instance_size;
163                 class->class_size = class->parent->class_size;
164         }
165
166         class->metadata_inited = 1;
167
168         /*
169          * Computes the size used by the fields, and their locations
170          */
171         if (class->field.count > 0){
172                 class->fields = g_new (MonoClassField, class->field.count);
173                 class_compute_field_layout (class);
174         }
175
176         if (!class->method.count)
177                 return;
178
179         class->methods = g_new (MonoMethod*, class->method.count);
180         for (i = class->method.first; i < class->method.last; ++i)
181                 class->methods [i - class->method.first] = 
182                         mono_get_method (class->image,
183                                          MONO_TOKEN_METHOD_DEF | (i + 1), 
184                                          class);
185 }
186
187 /**
188  * @image: context where the image is created
189  * @type_token:  typedef token
190  */
191 static MonoClass *
192 mono_class_create_from_typedef (MonoImage *image, guint32 type_token)
193 {
194         MonoTableInfo *tt = &image->tables [MONO_TABLE_TYPEDEF];
195         MonoClass *class;
196         guint32 cols [MONO_TYPEDEF_SIZE], parent_token;
197         guint tidx = mono_metadata_token_index (type_token);
198         const char *name, *nspace;
199      
200         g_assert (mono_metadata_token_table (type_token) == MONO_TABLE_TYPEDEF);
201
202         class = g_malloc0 (sizeof (MonoClass));
203
204         class->this_arg.byref = 1;
205         class->this_arg.data.klass = class;
206         class->this_arg.type = MONO_TYPE_CLASS;
207
208         mono_metadata_decode_row (tt, tidx-1, cols, CSIZE (cols));
209         class->name = name = mono_metadata_string_heap (image, cols[1]);
210         class->name_space = nspace = mono_metadata_string_heap (image, cols[2]);
211
212         class->image = image;
213         class->type_token = type_token;
214         class->flags = cols [0];
215
216         /*g_print ("Init class %s\n", name);*/
217
218         /* if root of the hierarchy */
219         if (!strcmp (nspace, "System") && !strcmp (name, "Object")) {
220                 class->parent = NULL;
221                 class->instance_size = sizeof (MonoObject);
222         } else if (!(cols [0] & TYPE_ATTRIBUTE_INTERFACE)) {
223                 parent_token = mono_metadata_token_from_dor (cols [3]);
224                 class->parent = mono_class_get (image, parent_token);
225                 class->valuetype = class->parent->valuetype;
226                 class->enumtype = class->parent->enumtype;
227         }
228
229         if (!strcmp (nspace, "System")) {
230                 if (!strcmp (name, "ValueType")) {
231                         class->valuetype = 1;
232                 } else if (!strcmp (name, "Enum")) {
233                         class->valuetype = 1;
234                         class->enumtype = 1;
235                 }
236         }
237         if (class->valuetype)
238                 class->this_arg.type = MONO_TYPE_VALUETYPE;
239         
240         /*
241          * Compute the field and method lists
242          */
243         class->field.first  = cols [MONO_TYPEDEF_FIELD_LIST] - 1;
244         class->method.first = cols [MONO_TYPEDEF_METHOD_LIST] - 1;
245
246         if (tt->rows > tidx){
247                 guint32 cols_next [MONO_TYPEDEF_SIZE];
248                 
249                 mono_metadata_decode_row (tt, tidx, cols_next, CSIZE (cols_next));
250                 class->field.last  = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1;
251                 class->method.last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1;
252         } else {
253                 class->field.last  = image->tables [MONO_TABLE_FIELD].rows;
254                 class->method.last = image->tables [MONO_TABLE_METHOD].rows;
255         }
256
257         if (cols [MONO_TYPEDEF_FIELD_LIST] && 
258             cols [MONO_TYPEDEF_FIELD_LIST] <= image->tables [MONO_TABLE_FIELD].rows)
259                 class->field.count = class->field.last - class->field.first;
260         else
261                 class->field.count = 0;
262
263         if (cols [MONO_TYPEDEF_METHOD_LIST] <= image->tables [MONO_TABLE_METHOD].rows)
264                 class->method.count = class->method.last - class->method.first;
265         else
266                 class->method.count = 0;
267
268         /* reserve space to store vector pointer in arrays */
269         if (!strcmp (nspace, "System") && !strcmp (name, "Array")) {
270                 class->instance_size += 2 * sizeof (gpointer);
271                 g_assert (class->field.count == 0);
272         }
273
274         class->interfaces = mono_metadata_interfaces_from_typedef (image, type_token);
275         return class;
276 }
277
278 MonoClass *
279 mono_class_from_mono_type (MonoType *type)
280 {
281         switch (type->type) {
282         case MONO_TYPE_OBJECT:
283                 return mono_defaults.object_class;
284         case MONO_TYPE_VOID:
285                 return mono_defaults.void_class;
286         case MONO_TYPE_BOOLEAN:
287                 return mono_defaults.boolean_class;
288         case MONO_TYPE_CHAR:
289                 return mono_defaults.char_class;
290         case MONO_TYPE_I1:
291                 return mono_defaults.byte_class;
292         case MONO_TYPE_U1:
293                 return mono_defaults.sbyte_class;
294         case MONO_TYPE_I2:
295                 return mono_defaults.int16_class;
296         case MONO_TYPE_U2:
297                 return mono_defaults.uint16_class;
298         case MONO_TYPE_I4:
299                 return mono_defaults.int32_class;
300         case MONO_TYPE_U4:
301                 return mono_defaults.uint32_class;
302         case MONO_TYPE_I:
303                 return mono_defaults.int_class;
304         case MONO_TYPE_U:
305                 return mono_defaults.uint_class;
306         case MONO_TYPE_I8:
307                 return mono_defaults.int64_class;
308         case MONO_TYPE_U8:
309                 return mono_defaults.uint64_class;
310         case MONO_TYPE_R4:
311                 return mono_defaults.single_class;
312         case MONO_TYPE_R8:
313                 return mono_defaults.double_class;
314         case MONO_TYPE_STRING:
315                 return mono_defaults.string_class;
316         case MONO_TYPE_ARRAY:
317                 return mono_defaults.array_class;
318         case MONO_TYPE_SZARRAY:
319         case MONO_TYPE_PTR:
320                 /* Not really sure about these. */
321                 return mono_class_from_mono_type (type->data.type);
322         case MONO_TYPE_CLASS:
323         case MONO_TYPE_VALUETYPE:
324                 return type->data.klass;
325         default:
326                 g_warning ("implement me %02x\n", type->type);
327                 g_assert_not_reached ();
328         }
329         
330         return NULL;
331 }
332
333 /**
334  * @image: context where the image is created
335  * @type_spec:  typespec token
336  * @at: an optional pointer to return the array type
337  */
338 static MonoClass *
339 mono_class_create_from_typespec (MonoImage *image, guint32 type_spec)
340 {
341         guint32 idx = mono_metadata_token_index (type_spec);
342         MonoTableInfo *t;
343         guint32 cols [MONO_TYPESPEC_SIZE];       
344         const char *ptr;
345         guint32 len;
346         MonoType *type;
347         MonoClass *class, *eclass;
348
349         t = &image->tables [MONO_TABLE_TYPESPEC];
350         
351         mono_metadata_decode_row (t, idx-1, cols, MONO_TYPESPEC_SIZE);
352         ptr = mono_metadata_blob_heap (image, cols [MONO_TYPESPEC_SIGNATURE]);
353         len = mono_metadata_decode_value (ptr, &ptr);
354         type = mono_metadata_parse_type (image, MONO_PARSE_TYPE, 0, ptr, &ptr);
355
356         switch (type->type) {
357         case MONO_TYPE_ARRAY:
358                 eclass = mono_class_from_mono_type (type->data.array->type);
359                 class = mono_array_class_get (eclass, type->data.array->rank);
360                 break;
361         case MONO_TYPE_SZARRAY:
362                 eclass = mono_class_from_mono_type (type->data.type);
363                 class = mono_array_class_get (eclass, 1);
364                 break;
365         default:
366                 g_warning ("implement me: %08x", type->type);
367                 g_assert_not_reached ();                
368         }
369
370         mono_metadata_free_type (type);
371         
372         return class;
373 }
374
375 /**
376  * mono_array_class_get:
377  * @eclass: element type class
378  * @rank: the dimension of the array class
379  *
380  * Returns: a class object describing the array with element type @etype and 
381  * dimension @rank. 
382  */
383 MonoClass *
384 mono_array_class_get (MonoClass *eclass, guint32 rank)
385 {
386         MonoImage *image;
387         MonoClass *class;
388         static MonoClass *parent = NULL;
389         MonoArrayClass *aclass;
390         guint32 key;
391
392         g_assert (rank <= 255);
393
394         if (!parent)
395                 parent = mono_defaults.array_class;
396
397         image = eclass->image;
398
399         g_assert (!eclass->type_token ||
400                   mono_metadata_token_table (eclass->type_token) == MONO_TABLE_TYPEDEF);
401         
402         key = ((rank & 0xff) << 24) | (eclass->type_token & 0xffffff);
403         if ((class = g_hash_table_lookup (image->array_cache, GUINT_TO_POINTER (key))))
404                 return class;
405         
406         aclass = g_new0 (MonoArrayClass, 1);
407         class = (MonoClass *)aclass;
408        
409         class->image = image;
410         class->name_space = "System";
411         class->name = "Array";
412         class->type_token = 0;
413         class->flags = TYPE_ATTRIBUTE_CLASS;
414         class->parent = parent;
415         class->instance_size = mono_class_instance_size (class->parent);
416         class->class_size = 0;
417
418         aclass->rank = rank;
419         aclass->element_class = eclass;
420         
421         g_hash_table_insert (image->array_cache, GUINT_TO_POINTER (key), class);
422         return class;
423 }
424
425 /**
426  * mono_class_instance_size:
427  * @klass: a class 
428  * 
429  * Returns: the size of an object instance
430  */
431 gint32
432 mono_class_instance_size (MonoClass *klass)
433 {
434         
435         if (!klass->metadata_inited)
436                 mono_class_metadata_init (klass);
437
438         return klass->instance_size;
439 }
440
441 /**
442  * mono_class_value_size:
443  * @klass: a class 
444  *
445  * This function is used for value types, and return the
446  * space and the alignment to store that kind of value object.
447  *
448  * Returns: the size of a value of kind @klass
449  */
450 gint32
451 mono_class_value_size      (MonoClass *klass, guint32 *align)
452 {
453         gint32 size;
454
455         /* fixme: check disable, because we still have external revereces to
456          * mscorlib and Dummy Objects 
457          */
458         /*g_assert (klass->valuetype);*/
459
460         size = mono_class_instance_size (klass) - sizeof (MonoObject);
461
462         if (align) {
463                 if (size <= 4)
464                         *align = 4;
465                 else
466                         *align = 8;
467         }
468
469         return size;
470 }
471
472 /**
473  * mono_class_data_size:
474  * @klass: a class 
475  * 
476  * Returns: the size of the static class data
477  */
478 gint32
479 mono_class_data_size (MonoClass *klass)
480 {
481         
482         if (!klass->metadata_inited)
483                 mono_class_metadata_init (klass);
484
485         return klass->class_size;
486 }
487
488 /*
489  * Auxiliary routine to mono_class_get_field
490  *
491  * Takes a field index instead of a field token.
492  */
493 static MonoClassField *
494 mono_class_get_field_idx (MonoClass *class, int idx)
495 {
496         if (class->field.count){
497                 if ((idx >= class->field.first) && (idx < class->field.last)){
498                         return &class->fields [idx - class->field.first];
499                 }
500         }
501
502         if (!class->parent)
503                 return NULL;
504         
505         return mono_class_get_field_idx (class->parent, idx);
506 }
507
508 /**
509  * mono_class_get_field:
510  * @class: the class to lookup the field.
511  * @field_token: the field token
512  *
513  * Returns: A MonoClassField representing the type and offset of
514  * the field, or a NULL value if the field does not belong to this
515  * class.
516  */
517 MonoClassField *
518 mono_class_get_field (MonoClass *class, guint32 field_token)
519 {
520         int idx = mono_metadata_token_index (field_token);
521
522         if (mono_metadata_token_code (field_token) == MONO_TOKEN_MEMBER_REF)
523                 g_error ("Unsupported Field Token is a MemberRef, implement me");
524
525         g_assert (mono_metadata_token_code (field_token) == MONO_TOKEN_FIELD_DEF);
526
527         return mono_class_get_field_idx (class, idx - 1);
528 }
529
530 MonoClassField *
531 mono_class_get_field_from_name (MonoClass *klass, const char *name)
532 {
533         int i;
534         guint32 token;
535         MonoTableInfo *t = &klass->image->tables [MONO_TABLE_FIELD];
536
537         for (i = 0; i < klass->field.count; ++i) {
538                 token = mono_metadata_decode_row_col (t, klass->field.first + i, MONO_FIELD_NAME);
539                 if (strcmp (name, mono_metadata_string_heap (klass->image, token)) == 0)
540                         return &klass->fields [i];
541         }
542         return NULL;
543 }
544
545 /**
546  * mono_class_get:
547  * @image: the image where the class resides
548  * @type_token: the token for the class
549  * @at: an optional pointer to return the array element type
550  *
551  * Returns: the MonoClass that represents @type_token in @image
552  */
553 MonoClass *
554 mono_class_get (MonoImage *image, guint32 type_token)
555 {
556         MonoClass *class;
557
558         switch (type_token & 0xff000000){
559         case MONO_TOKEN_TYPE_DEF:
560                 if ((class = g_hash_table_lookup (image->class_cache, 
561                                                   GUINT_TO_POINTER (type_token))))
562                         return class;
563                 class = mono_class_create_from_typedef (image, type_token);
564                 break;          
565         case MONO_TOKEN_TYPE_REF:
566                 return mono_class_create_from_typeref (image, type_token);
567         case MONO_TOKEN_TYPE_SPEC:
568                 if ((class = g_hash_table_lookup (image->class_cache, 
569                                                   GUINT_TO_POINTER (type_token))))
570                         return class;
571
572                 class = mono_class_create_from_typespec (image, type_token);
573                 break;
574         default:
575                 g_assert_not_reached ();
576         }
577         
578         g_hash_table_insert (image->class_cache, GUINT_TO_POINTER (type_token), class);
579
580         return class;
581 }
582
583 MonoClass *
584 mono_class_from_name (MonoImage *image, const char* name_space, const char *name)
585 {
586         GHashTable *nspace_table;
587         guint32 token;
588
589         nspace_table = g_hash_table_lookup (image->name_cache, name_space);
590         if (!nspace_table)
591                 return 0;
592         token = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, name));
593         
594         if (!token) {
595                 g_warning ("token not found for %s.%s in image %s", name_space, name, image->name);
596                 return NULL;
597         }
598
599         token = MONO_TOKEN_TYPE_DEF | token;
600
601         return mono_class_get (image, token);
602 }
603
604 /**
605  * mono_array_element_size:
606  * @ac: pointer to a #MonoArrayClass
607  *
608  * Returns: the size of single array element.
609  */
610 gint32
611 mono_array_element_size (MonoArrayClass *ac)
612 {
613         gint32 esize;
614
615         esize = mono_class_instance_size (ac->element_class);
616         
617         if (ac->element_class->valuetype)
618                 esize -= sizeof (MonoObject);
619         
620         return esize;
621 }