df71fcaee068befcc4f773b8d08315b8d908db44
[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/cli/cli.h>
25 #include <mono/cli/class.h>
26 #include <mono/cli/types.h>
27 #include <mono/cli/object.h>
28
29 #define CSIZE(x) (sizeof (x) / 4)
30
31 static void
32 typedef_from_typeref (MonoImage *image, guint32 type_token, MonoImage **rimage, guint32 *index)
33 {
34         guint32 cols[MONO_TYPEDEF_SIZE];
35         MonoMetadata *m = &image->metadata;
36         MonoTableInfo  *t = &m->tables[MONO_TABLE_TYPEREF];
37         guint32 idx, i;
38         const char *name, *nspace;
39         
40         mono_metadata_decode_row (t, (type_token&0xffffff)-1, cols, 3);
41         g_assert ((cols [0] & 0x3) == 2);
42         idx = cols [0] >> 2;
43         name = mono_metadata_string_heap (m, cols [1]);
44         nspace = mono_metadata_string_heap (m, cols [2]);
45         /* load referenced assembly */
46         image = image->references [idx-1]->image;
47         m = &image->metadata;
48         t = &m->tables [MONO_TABLE_TYPEDEF];
49         /* dumb search for now */
50         for (i=0; i < t->rows; ++i) {
51                 mono_metadata_decode_row (t, i, cols, MONO_TYPEDEF_SIZE);
52
53                 if (!strcmp (name, mono_metadata_string_heap (m, cols [1])) &&
54                     !strcmp (nspace, mono_metadata_string_heap (m, cols [2]))) {
55                         *rimage = image;
56                         *index =  MONO_TOKEN_TYPE_DEF | (i + 1);
57                         return;
58                 }
59         }
60         g_assert_not_reached ();
61         
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 (MonoMetadata *m, MonoClass *class)
76 {
77         const int top = class->field.count;
78         guint32 layout = class->flags & TYPE_ATTRIBUTE_LAYOUT_MASK;
79         MonoTableInfo *t = &m->tables [MONO_TABLE_FIELD];
80         int i, j;
81
82         /*
83          * Fetch all the field information.
84          */
85         for (i = 0; i < top; i++){
86                 const char *sig;
87                 guint32 cols [3];
88                 int idx = class->field.first + i;
89                 
90                 mono_metadata_decode_row (t, idx, cols, CSIZE (cols));
91                 sig = mono_metadata_blob_heap (m, cols [2]);
92                 mono_metadata_decode_value (sig, &sig);
93                 /* FIELD signature == 0x06 */
94                 g_assert (*sig == 0x06);
95                 class->fields [i].type = mono_metadata_parse_field_type (
96                         m, sig + 1, &sig);
97                 class->fields [i].flags = cols [0];
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->type, &align);
109                         if (class->fields [i].flags & 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->type, &align);
137                         if (class->fields [i].flags & 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 /**
152  * @image: context where the image is created
153  * @type_token:  typedef token
154  */
155 static MonoClass *
156 mono_class_create_from_typedef (MonoImage *image, guint32 type_token)
157 {
158         MonoMetadata *m = &image->metadata;
159         MonoTableInfo *tt = &m->tables [MONO_TABLE_TYPEDEF];
160         MonoClass stack_class;
161         MonoClass *class = &stack_class;
162         guint32 cols [MONO_TYPEDEF_SIZE], parent_token;
163         guint tidx = mono_metadata_token_index (type_token);
164         const char *name, *nspace;
165      
166         g_assert (mono_metadata_token_table (type_token) == MONO_TABLE_TYPEDEF);
167
168         memset (class, 0, sizeof (MonoClass));
169
170         mono_metadata_decode_row (tt, tidx-1, cols, CSIZE (cols));
171         name = mono_metadata_string_heap (m, cols[1]);
172         nspace = mono_metadata_string_heap (m, cols[2]);
173         /*g_print ("Init class %s\n", name);*/
174  
175         /* if root of the hierarchy */
176         if (!strcmp (nspace, "System") && !strcmp (name, "Object")) {
177                 class->instance_size = sizeof (MonoObject);
178                 class->parent = NULL;
179         } else {
180                 parent_token = mono_metadata_token_from_dor (cols [3]);
181                 class->parent = mono_class_get (image, parent_token);
182                 class->instance_size = class->parent->instance_size;
183                 class->valuetype = class->parent->valuetype;
184         }
185         if (!strcmp (nspace, "System") && !strcmp (name, "ValueType"))
186                 class->valuetype = 1;
187
188         g_assert (class->instance_size);
189         class->image = image;
190         class->type_token = type_token;
191         class->flags = cols [0];
192         class->class_size = sizeof (MonoClass);
193         
194         /*
195          * Compute the field and method lists
196          */
197         class->field.first  = cols [MONO_TYPEDEF_FIELD_LIST] - 1;
198         class->method.first = cols [MONO_TYPEDEF_METHOD_LIST] - 1;
199
200         if (tt->rows > tidx){
201                 guint32 cols_next [MONO_TYPEDEF_SIZE];
202                 
203                 mono_metadata_decode_row (tt, tidx, cols_next, CSIZE (cols_next));
204                 class->field.last  = cols_next [MONO_TYPEDEF_FIELD_LIST] - 1;
205                 class->method.last = cols_next [MONO_TYPEDEF_METHOD_LIST] - 1;
206         } else {
207                 class->field.last  = m->tables [MONO_TABLE_FIELD].rows;
208                 class->method.last = m->tables [MONO_TABLE_METHOD].rows;
209         }
210
211         if (cols [MONO_TYPEDEF_FIELD_LIST] && 
212             cols [MONO_TYPEDEF_FIELD_LIST] <= m->tables [MONO_TABLE_FIELD].rows)
213                 class->field.count = class->field.last - class->field.first;
214         else
215                 class->field.count = 0;
216
217         if (cols [MONO_TYPEDEF_METHOD_LIST] <= m->tables [MONO_TABLE_METHOD].rows)
218                 class->method.count = class->method.last - class->method.first;
219         else
220                 class->method.count = 0;
221
222         /*
223          * Computes the size used by the fields, and their locations
224          */
225         if (class->field.count > 0){
226                 class->fields = g_new (MonoClassField, class->field.count);
227                 class_compute_field_layout (m, class);
228         }
229
230         /* reserve space to store vector pointer in arrays */
231         if (!strcmp (nspace, "System") && !strcmp (name, "Array")) {
232                 class->instance_size += 2 * sizeof (gpointer);
233                 g_assert (class->field.count == 0);
234                 g_assert (class->instance_size == sizeof (MonoArrayObject));
235         }
236
237         class = g_malloc0 (class->class_size);
238         *class = stack_class;
239         return class;
240 }
241
242 static guint32
243 mono_type_to_tydedef (MonoImage *image, MonoType *type, MonoImage **rimage)
244 {
245         MonoImage *corlib, *res;
246         guint32 etype;
247
248         res = corlib = mono_get_corlib ();
249
250         switch (type->type) {
251         case MONO_TYPE_BOOLEAN:
252                 etype = mono_typedef_from_name (corlib, "Boolean", "System", NULL);
253                 break;
254         case MONO_TYPE_CHAR:
255                 etype = mono_typedef_from_name (corlib, "Char", "System", NULL); 
256                 break;
257         case MONO_TYPE_I1:
258                 etype = mono_typedef_from_name (corlib, "Byte", "System", NULL); 
259                 break;
260         case MONO_TYPE_I2:
261                 etype = mono_typedef_from_name (corlib, "Int16", "System", NULL); 
262                 break;
263         case MONO_TYPE_U2:
264                 etype = mono_typedef_from_name (corlib, "UInt16", "System", NULL); 
265                 break;
266         case MONO_TYPE_I4:
267                 etype = mono_typedef_from_name (corlib, "Int32", "System", NULL); 
268                 break;
269         case MONO_TYPE_U4:
270                 etype = mono_typedef_from_name (corlib, "UInt32", "System", NULL); 
271                 break;
272         case MONO_TYPE_I8:
273                 etype = mono_typedef_from_name (corlib, "Int64", "System", NULL); 
274                 break;
275         case MONO_TYPE_U8:
276                 etype = mono_typedef_from_name (corlib, "UInt64", "System", NULL); 
277                 break;
278         case MONO_TYPE_R8:
279                 etype = mono_typedef_from_name (corlib, "Double", "System", NULL); 
280                 break;
281         case MONO_TYPE_STRING:
282                 etype = mono_typedef_from_name (corlib, "String", "System", NULL); 
283                 break;
284         case MONO_TYPE_CLASS:
285                 etype = type->data.token;
286                 res = image;
287                 break;
288         default:
289                 g_warning ("implement me %08x\n", type->type);
290                 g_assert_not_reached ();
291         }
292         
293         *rimage = res;
294         return etype;
295 }
296
297 /**
298  * @image: context where the image is created
299  * @type_spec:  typespec token
300  * @at: an optional pointer to return the array type
301  */
302 static MonoClass *
303 mono_class_create_from_typespec (MonoImage *image, guint32 type_spec)
304 {
305         MonoMetadata *m = &image->metadata;
306         guint32 idx = mono_metadata_token_index (type_spec);
307         MonoTableInfo *t;
308         guint32 cols [MONO_TYPESPEC_SIZE];       
309         const char *ptr;
310         guint32 len, etype;
311         MonoType *type;
312         MonoClass *class;
313         MonoImage *rimage;
314
315         t = &m->tables [MONO_TABLE_TYPESPEC];
316         
317         mono_metadata_decode_row (t, idx-1, cols, MONO_TYPESPEC_SIZE);
318         ptr = mono_metadata_blob_heap (m, cols [MONO_TYPESPEC_SIGNATURE]);
319         len = mono_metadata_decode_value (ptr, &ptr);
320         type = mono_metadata_parse_type (m, ptr, &ptr);
321
322         switch (type->type) {
323         case MONO_TYPE_ARRAY:
324                 etype = mono_type_to_tydedef (image, type->data.array->type, &rimage);
325                 class = mono_array_class_get (rimage, etype, type->data.array->rank);
326                 break;
327         case MONO_TYPE_SZARRAY:
328                 g_assert (!type->custom_mod);
329                 etype = mono_type_to_tydedef (image, type->data.type, &rimage);
330                 class = mono_array_class_get (rimage, etype, 1);
331                 break;
332         default:
333                 g_assert_not_reached ();                
334         }
335
336         mono_metadata_free_type (type);
337         
338         return class;
339 }
340
341 MonoClass *
342 mono_array_class_get (MonoImage *image, guint32 etype, guint32 rank)
343 {
344         MonoClass *class, *eclass;
345         static MonoClass *parent = NULL;
346         MonoArrayClass *aclass;
347         guint32 esize, key;
348
349         g_assert (rank <= 255);
350
351         if (!parent) {
352                 guint32 arr_token;
353                 MonoImage *corlib;
354
355                 mono_get_array_class_info (&arr_token, &corlib);
356                 parent = mono_class_get (corlib, arr_token);
357                 g_assert (parent != NULL);
358         }
359
360         eclass = mono_class_get (image, etype);
361         g_assert (eclass != NULL);
362
363         image = eclass->image;
364
365         esize = eclass->instance_size;
366
367         g_assert (!eclass->type_token ||
368                   mono_metadata_token_table (eclass->type_token) == MONO_TABLE_TYPEDEF);
369         
370         key = ((rank & 0xff) << 24) | (eclass->type_token & 0xffffff);
371         if ((class = g_hash_table_lookup (image->array_cache, GUINT_TO_POINTER (key))))
372                 return class;
373         
374         if (eclass->valuetype)
375                 esize -= sizeof (MonoObject);
376
377         aclass = g_new0 (MonoArrayClass, 1);
378         class = (MonoClass *)aclass;
379        
380         class->image = image;
381         class->type_token = 0;
382         class->flags = TYPE_ATTRIBUTE_CLASS;
383         class->parent = parent;
384         class->instance_size = class->parent->instance_size;
385         class->class_size = sizeof (MonoArrayClass);
386         
387         aclass->rank = rank;
388         aclass->etype_token = eclass->type_token;
389         aclass->esize = esize;
390
391         g_hash_table_insert (image->array_cache, GUINT_TO_POINTER (key), class);
392         return class;
393 }
394
395 /*
396  * Auxiliary routine to mono_class_get_field
397  *
398  * Takes a field index instead of a field token.
399  */
400 static MonoClassField *
401 mono_class_get_field_idx (MonoClass *class, int idx)
402 {
403         if (class->field.count){
404                 if ((idx >= class->field.first) && (idx < class->field.last)){
405                         return &class->fields [idx - class->field.first];
406                 }
407         }
408
409         if (!class->parent)
410                 return NULL;
411         
412         return mono_class_get_field_idx (class->parent, idx);
413 }
414
415 /**
416  * mono_class_get_field:
417  * @class: the class to lookup the field.
418  * @field_token: the field token
419  *
420  * Returns: A MonoClassField representing the type and offset of
421  * the field, or a NULL value if the field does not belong to this
422  * class.
423  */
424 MonoClassField *
425 mono_class_get_field (MonoClass *class, guint32 field_token)
426 {
427         int idx = mono_metadata_token_index (field_token);
428
429         if (mono_metadata_token_code (field_token) == MONO_TOKEN_MEMBER_REF)
430                 g_error ("Unsupported Field Token is a MemberRef, implement me");
431
432         g_assert (mono_metadata_token_code (field_token) == MONO_TOKEN_FIELD_DEF);
433
434         return mono_class_get_field_idx (class, idx - 1);
435 }
436
437 /**
438  * mono_class_get:
439  * @image: the image where the class resides
440  * @type_token: the token for the class
441  * @at: an optional pointer to return the array element type
442  *
443  * Returns: the MonoClass that represents @type_token in @image
444  */
445 MonoClass *
446 mono_class_get (MonoImage *image, guint32 type_token)
447 {
448         MonoClass *class;
449        
450         switch (type_token & 0xff000000){
451         case MONO_TOKEN_TYPE_DEF:
452                 if ((class = g_hash_table_lookup (image->class_cache, 
453                                                   GUINT_TO_POINTER (type_token))))
454                         return class;
455
456                 class = mono_class_create_from_typedef (image, type_token);
457                 break;
458                 
459         case MONO_TOKEN_TYPE_REF: {
460                 typedef_from_typeref (image, type_token, &image, &type_token);
461                 return mono_class_get (image, type_token);
462         }
463         case MONO_TOKEN_TYPE_SPEC:
464                 if ((class = g_hash_table_lookup (image->class_cache, 
465                                                   GUINT_TO_POINTER (type_token))))
466                         return class;
467
468                 class = mono_class_create_from_typespec (image, type_token);
469                 break;
470         default:
471                 g_assert_not_reached ();
472         }
473         
474         g_hash_table_insert (image->class_cache, GUINT_TO_POINTER (type_token), class);
475
476         return class;
477 }
478